HookStack
Back to catalogue
ValidationPreToolUse· Write|EditPreToolUseBefore tool execution · can block⚡ blocking

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

#validation#files#build-artifacts#node_modules#guardrail

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));
}

Learn more

Related hooks