HookStackGitHub
Back to catalogue
SecurityPreToolUse· Write|EditPreToolUseBefore tool execution · can block⚡ blocking

Block file writes on main branch

Blocks any Write or Edit tool call when the current branch is main/master and the working directory is the principal repo (not a worktree). Forces the agent to create a branch or worktree before making file changes.

Use cases

  • Accidental main commits prevention
  • Branch discipline enforcement
  • Pair with worktree auto-create hook
  • Strict repository governance

Providers & tags

Claude Code
#git#safety#main-branch#write-guard#branch-discipline

settings.json fragment

{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [
          {
            "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/pre-write-main-guard.mjs",
            "type": "command"
          }
        ],
        "matcher": "Write|Edit"
      }
    ]
  }
}

Script · .claude/hooks/pre-write-main-guard.mjs

#!/usr/bin/env node
// PreToolUse Write|Edit: bloque la première écriture sur main si aucun worktree n'est actif
import { readFileSync } from 'fs';
import { execSync } from 'child_process';
import { fileURLToPath } from 'url';

function defaultExec(cmd) {
  try { return execSync(cmd, { encoding: 'utf8', timeout: 5_000 }).trim(); } catch { return ''; }
}

export function run(input, { exec = defaultExec } = {}) {
  const branch = exec('git branch --show-current') || exec('git rev-parse --abbrev-ref HEAD');
  if (!branch || !/^(main|master)$/.test(branch)) return null;

  const worktreeList = exec('git worktree list');
  const currentRoot = exec('git rev-parse --show-toplevel');
  const mainRoot = worktreeList.split('\n')[0]?.split(/\s+/)[0] ?? '';
  if (mainRoot !== currentRoot) return null;

  const filePath = input.tool_input?.file_path ?? '(fichier inconnu)';

  // Autoriser les écritures vers des fichiers dans un worktree (hors du repo principal)
  if (filePath !== '(fichier inconnu)' && !filePath.startsWith(mainRoot + '/')) return null;

  return {
    decision: 'block',
    reason: `Écriture sur \`${branch}\` bloquée : vous êtes sur la branche principale.\nCréez un worktree (\`git worktree add ../mon-fix -b feat/mon-fix\`) ou changez de branche avant de modifier \`${filePath}\`.`,
  };
}

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