Block edits to generated folders
Never waste an edit on a file the build overwrites
Blocks writes into generated directories — node_modules, .next, dist, build, coverage, __pycache__, .venv and friends — by checking the target path. Stops the agent from "fixing" an artifact that the next build silently regenerates.
What does the Block edits to generated folders hook do?
Block edits to generated folders is a Claude Code PreToolUse hook matching Write|Edit. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. Never waste an edit on a file the build overwrites.
As a PreToolUse hook it runs before the action completes, so it can block or adjust what Claude is about to do. 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
- Avoid wasted work
- Source-of-truth hygiene
- Cleaner diffs
Tags
settings.json fragment
{
"hooks": {
"PreToolUse": [
{
"hooks": [
{
"command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/block-generated-paths.mjs",
"type": "command"
}
],
"matcher": "Write|Edit"
}
]
}
}Script · .claude/hooks/block-generated-paths.mjs
#!/usr/bin/env node
// Bloque l'écriture dans un dossier généré (node_modules, dist, .next…) (PreToolUse Write|Edit)
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
// Segments de chemin qui ne contiennent que des artefacts générés : éditer ici = travail perdu.
const GENERATED = /(?:^|\/)(node_modules|\.next|\.nuxt|\.svelte-kit|dist|build|out|coverage|\.turbo|\.cache|__pycache__|\.venv|\.pytest_cache|\.mypy_cache)(?:\/|$)/;
export function run(input) {
const filePath = input.tool_input?.file_path ?? '';
if (!filePath) return null;
const hit = filePath.match(GENERATED);
if (!hit) return null;
return {
decision: 'block',
reason:
`Écriture bloquée dans un répertoire généré ('${hit[1]}') : ${filePath}. ` +
'Modifiez la source, pas l\'artefact de build — il sera écrasé au prochain build. ' +
'Si c\'est intentionnel, faites-le manuellement.',
};
}
/* 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));
}