Block push on closed PR
prevents accidentally pushing to a merged or closed PR branch
Intercepts git push commands and checks via gh CLI if the current branch has a closed or merged PR. Blocks the push with a message pointing to create a new branch from main.
What does the Block push on closed PR hook do?
Block push on closed PR is a Claude Code PreToolUse hook matching Bash. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. prevents accidentally pushing to a merged or closed PR branch.
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
- Prevent pushing fixes on a branch whose PR was already merged or closed
- Guide the agent to always branch from an up-to-date main
Tags
settings.json fragment
{
"hooks": {
"PreToolUse": [
{
"hooks": [
{
"command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/block-push-closed-pr.mjs",
"type": "command"
}
],
"matcher": "Bash"
}
]
}
}Script · .claude/hooks/block-push-closed-pr.mjs
import { execSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
const defaultExec = (cmd) => execSync(cmd, { encoding: "utf8", timeout: 5000 });
export function run(input, { exec = defaultExec } = {}) {
const command = input?.tool_input?.command ?? "";
if (!/\bgit\s+push\b/.test(command)) return null;
let branch;
try {
branch = exec("git rev-parse --abbrev-ref HEAD").trim();
} catch {
return null;
}
if (!branch || branch === "HEAD" || branch === "main" || branch === "master")
return null;
let state;
try {
state = exec(`gh pr view "${branch}" --json state --jq '.state'`).trim();
} catch {
return null;
}
if (state === "CLOSED" || state === "MERGED") {
const label = state === "MERGED" ? "mergée" : "fermée";
return {
decision: "block",
reason: `La PR de la branche '${branch}' est ${label}. Créez une nouvelle branche depuis main : git checkout -b fix/... origin/main`,
};
}
return null;
}
/* 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));
}