Hook in workflow
git-guard
A PreToolUse hook for Bash that blocks git operations which destroy work or bypass review, with a reason Claude can act on. Stops force-pushes and deletions of protected branches (main, master, develop, or a configured list with globs), commit --no-verify, git clean -f, stash drop and clear, and hard resets, checkouts, or restores that would discard uncommitted changes. Splits compound commands so nothing hides behind a prefix. Use when Claude has git access and a mistake would cost shared history or uncommitted work.
Install
As a plugin
Bundled in the workflow-hooks plugin. Add the marketplace once, then install the bundle; it updates with the marketplace.
/plugin marketplace add KhaledSaeed18/dotclaude/plugin install workflow-hooks@dotclaudeAs a single item
Run from the project root; the hook lands under .claude/. Copy it to ~/.claude/ to make it global.
npx shadcn@latest add KhaledSaeed18/dotclaude/git-guardInstalled as a single item, a hook still needs the settings block documented below added to .claude/settings.json. The plugin route wires it automatically.
Files installed
.claude/hooks/git-guard/HOOK.md.claude/hooks/git-guard/git-guard.mjs
What it does
git-guard
A Claude Code hook that guards the git footguns command-guard does not cover. It is not about catastrophic shell commands (that is command-guard and smart-approve); it is about the ordinary git operations that quietly lose work or route around the review process.
What it blocks
| Operation | Condition | Why |
|---|---|---|
git push --force / -f / --force-with-lease / +refspec |
target (or current) branch is protected | rewrites shared history |
git push --delete <branch> / git push origin :<branch> |
branch is protected | deletes the branch remotely |
git branch -D / -d <branch> |
branch is protected | deletes the branch locally |
git commit --no-verify / -n |
always (configurable) | skips the project's own pre-commit hooks |
git reset --hard, git checkout ., git restore . |
working tree has uncommitted changes | discards work irreversibly |
git clean -f |
always | deletes untracked files |
git stash drop / clear |
always | discards stashed work |
Protected branches default to main, master, and develop. Patterns may contain * (release/*). Compound commands (&&, ||, ;, |) are split and each part is checked; quoted strings are respected, so echo "git push --force" passes.
The block message names the operation and the safer alternative (feature branch and PR, stash first, git clean -n).
Files
| File | Purpose |
|---|---|
git-guard.mjs |
The hook script. |
HOOK.md |
This file. |
Activate it (required manual step)
Add this to .claude/settings.json (project) or ~/.claude/settings.json (global):
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/git-guard/git-guard.mjs\""
}
]
}
]
}
}
Installing the workflow-hooks plugin from the dotclaude marketplace wires this automatically.
Verify it
echo '{"tool_name":"Bash","tool_input":{"command":"git push --force origin main"},"cwd":"'$PWD'"}' \
| node .claude/hooks/git-guard/git-guard.mjs; echo "exit=$?"
Exit 2 with git-guard blocked: on stderr means it is active.
Tune it
{
"gitGuard": {
"protectedBranches": ["main", "master", "develop", "release/*"],
"allowNoVerify": false,
"allowForcePush": false
}
}
allowForcePush: true disables the force-push rule only; deletions and the other rules still apply.
Hooks run arbitrary commands on your machine with your credentials whenever their event fires. Read any hook script (including this one) before enabling it.
This is the hook as installed, rendered from its source on GitHub. Read it as Markdown.
More in workflow
- branch-protectA UserPromptSubmit hook that, once per session, tells Claude the checkout is on a protected branch (main, master, develop, or a configured list) so it...
- session-contextA SessionStart hook that orients every new session before the first prompt by injecting the current branch, ahead/behind status, uncommitted files, the...
- stop-gateA Stop hook that runs the project's test command before Claude may end a turn with uncommitted changes, blocking the stop and returning the failure output...
- subagent-summaryA SubagentStop hook that appends one JSON line per finished subagent to a project log (timestamp, session, agent id, and the first 300 characters of its...
- typecheck-on-editA PostToolUse hook that type-checks the project after Claude edits a TypeScript, Python, or Go file and returns the errors as context immediately, listing...