Dev.to · 8 min read

We ran 28 git commands through Claude Code's most-installed git guardrail

We ran 28 git commands through Claude Code's most-installed git guardrail

The most-installed git skill for Claude Code is git-guardrails-claude-code, Matt Pocock's git safety skill, with 523,036 all-time installs on the skills.sh registry as of November 7, 2026. It lives in mattpocock/skills on GitHub (317,373 stars the same day). It does one job. In the words of its SKILL.md, it "Sets up a PreToolUse hook that intercepts and blocks dangerous git commands before Claude executes them." Source: mattpocock/skills, skills/misc/git-guardrails-claude-code/SKILL.md A hook is a better bet than a line in CLAUDE.md, because a hook is code that runs on every tool call and a prompt rule can fall out of a long context. But a hook is only as good as its patterns. So we read the bundled script and tested it the way Claude Code calls it. Short version: it catches chained and wrapped pushes like ls && git push, but git -C . push, a double space, git clean -xfd, any git alias, and every command on a machine without jq get through. Disclosure: we run Skillselion. Install counts come from our listing pages (live catalog, refreshed daily from skills.sh, GitHub and MCP registries, ranked by real installs). This post is a technical cut of our git skills for Claude Code guide. How does git-guardrails-claude-code work? The skill copies block-dangerous-git.sh into .claude/hooks/ (one project) or ~/.claude/hooks/ (all projects), then registers it under hooks.PreToolUse with a Bash matcher. Every time Claude wants to run a shell command, Claude Code sends the hook a JSON payload on stdin. The command string sits at .tool_input.command. Per the Claude Code hooks reference, exit code 2 from a PreToolUse hook blocks the tool call, and the hook's stderr is shown to Claude. The current block-dangerous-git.sh is 25 lines. It reads the command with jq, then loops over this list and runs grep -qE against each entry: DANGEROUS_PATTERNS=( "git push" "git reset --hard" "git clean -fd" "git clean -f" "git branch -D" "git checkout \." "git restore \." "push --force" "reset --hard" ) On a match it prints to stderr and exits 2: BLOCKED: 'git push origin main' matches dangerous pattern 'git push'. The user has prevented you from doing this. So this is substring matching on the raw command string. That design choice explains every row in the table below. The test harness We fed each command to the script exactly as a PreToolUse hook receives it. Copy this if you want to run your own list: #!/bin/bash # usage: ./test-hook.sh path/to/block-dangerous-git.sh < commands.txt HOOK="$1" while IFS= read -r cmd; do [ -z "$cmd" ] && continue jq -nc --arg c "$cmd" '{tool_input:{command:$c}}' | bash "$HOOK" >/dev/null 2>&1 code=$? [ $code -eq 2 ] && v=BLOCKED || v=passes printf '%s\t%s\t%s\n' "$code" "$v" "$cmd" done We ran it on macOS against the upstream script as of November 7, 2026 (last commit to that skill folder: August 19, 2026). The SKILL.md's own self-test, piping git push origin main and expecting exit 2, passes. Which git commands does the hook block? Command Exit Result git push origin main 2 blocked git push --force-with-lease 2 blocked ls && git push 2 blocked command git push 2 blocked bash -c "git push" 2 blocked git -C . push 0 passes git push origin main (two spaces) 0 passes git reset --hard HEAD~1 2 blocked git clean -fd / git clean -fdx 2 blocked git clean -xfd 0 passes git branch -D x 2 blocked git branch --delete --force x 0 passes git checkout . / git restore . 2 blocked git checkout -- . 0 passes git restore src/ 0 passes git checkout -f main 0 passes git stash drop / git stash clear 0 passes git rebase -i HEAD~3 0 passes git commit --amend --no-edit 0 passes git reflog expire --expire=now --all 0 passes git config alias.p push, then git p 0 / 0 passes git log --grep="reset --hard" 2 blocked git commit -m "docs: never git push from CI" 2 blocked git status 0 passes What the numbers say: Chaining does not help Claude. Because the match is a substring, ls && git push, bash -c "git push" and command git push are all caught. That is the strong side of the design. Anything that splits the literal string gets through, although the SKILL.md lists git push as blocked in "all variants including --force". A global option between git and the subcommand (-C, -c), a second space, a reordered flag cluster (-xfd instead of -fd...), or the long form of a flag (--delete --force for -D). Some history-losing commands are not on the list. git stash drop, git stash clear, git checkout -- . and git reflog expire all exit 0. The SKILL.md never claims to cover them, so treat this as scope, not a bug. Aliases are invisible. A string matcher cannot know that git p means push. Any alias already in your ~/.gitconfig passes. It errs toward blocking. A commit message or a --grep that contains a pattern gets blocked. Annoying, but the safe direction. It fails open without jq. With jq removed from PATH, the script prints jq: command not found and exits 0, so git push origin main passes. Our test Mac has /usr/bin/jq built in; check that your containers and CI images do too. Add these patterns if your workflow needs them The skill's step 4 tells Claude to ask whether you want to add or remove patterns, so extending it is the intended path. This is the variant we tested. It closes every gap in the table except --amend, which we left open on purpose, and aliases that already exist: #!/bin/bash command -v jq >/dev/null || { echo "BLOCKED: jq not installed, git guard cannot read the command." >&2; exit 2; } INPUT=$(cat) COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command' | tr -s '[:space:]' ' ') DANGEROUS_PATTERNS=( # ...the nine upstream patterns, unchanged... "git (-[cC] [^ ]+ )+push" "git clean -[a-zA-Z]*f" "git branch .*--delete --force" "git checkout (-- )?\.( |$)" "git checkout -f" "git stash (drop|clear)" "git rebase -i" "git reflog expire" "git config .*alias\." ) # ...same loop as upstream... With that version, 24 of our 28 commands exit 2. The four that pass are git restore src/, git commit --amend --no-edit, git status, and git p from an alias that already exists. Blocking amend is a judgment call. If Claude amends a commit you already pushed, the next push needs force, and the guard stops that push anyway. For the alias gap, the real backstop sits on the server: a branch protection rule or ruleset on main that rejects force pushes. A client-side hook decides what Claude may try. The remote decides what can land. The rest of a git stack for Claude Code A few other skills shape how Claude commits, isolates work and opens PRs. We checked each current SKILL.md on GitHub before describing it. Commits. caveman-commit, the terse Conventional Commits skill (422,617 installs; juliusbrussee/caveman on GitHub) aims for subjects of 50 characters or fewer with a hard cap of 72, and leaves out AI attribution lines unless your own rules require one. It never touches the repo: "Does not run git commit, does not stage files, does not amend." Our caveman skill guide covers the rest of that pack. If you want staging too, the git-commit skill from awesome-copilot (47,830 installs; github/awesome-copilot) does both. Source: juliusbrussee/caveman, skills/caveman-commit/SKILL.md Worktrees. using-git-worktrees, obra's workspace isolation skill (223,088 installs; obra/superpowers) first checks whether the session already runs in a linked worktree, prefers a native worktree tool, and only then falls back to git worktree add. Its core principle ends with "Never fight the harness." Two agents in one checkout share one index, so this is the skill that keeps their git add calls apart. Source: obra/superpowers, skills/using-git-worktrees/SKILL.md Pull requests. create-pull-request, Cline's gh PR skill (810 installs; cline/cline) checks gh --version, pushes with git push origin HEAD, and passes the body to gh pr create --body-file from a temp file. Note the interaction: with the guardrail installed, that push step exits 2, so you push and Claude opens the PR. Stacked PRs. graphite, Graphite's stacked-PR skill (9,977 installs; withgraphite/agent-skills) is a quick reference for gt create, gt restack and gt submit. We ran gt submit --no-interactive through the stock hook: exit 0. It contains no git push string, so if your rule is "Claude never pushes", add gt submit to the list. gh pr create --fill and glab mr create --fill also exit 0. If a PR CLI in your setup can push a branch for you, the hook will not see that push, so read its flags and add it to the list if needed. For habits rather than rules, git-workflow-and-versioning, Addy Osmani's git practice skill (63,600 installs; addyosmani/agent-skills) treats commits as save points. The wider set is ranked on our best dev tools skills page and in the dev tools category. If Claude talks to GitHub through an API instead of gh, see the best GitHub MCP servers ranking; a Bash-matcher hook never sees those calls. Install the guardrail, run the harness against the commands your team actually types, and add what is missing. The full list with install counts is in our Claude Code git skills guide.

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More AI & Machine Learning News