dotclaude

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@dotclaude

As 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-guard

Installed 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.

  • 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...