perf(hooks): narrow Bash hook matchers to git commands only#655
perf(hooks): narrow Bash hook matchers to git commands only#655carlos-alm wants to merge 3 commits intomainfrom
Conversation
Bash hooks (guard-git, pre-commit, lint-staged, check-readme, guard-pr-body, track-moves, post-git-ops) now use `Bash(git )` matcher instead of `Bash`, so they only fire on git commands rather than every Bash invocation — reduces token consumption.
|
Claude finished @carlos-alm's task —— View job PR Review Complete ⭐⭐⭐⭐☆Todo List:
Root Cause AnalysisProblem Being Solved: Excessive token consumption due to git-specific hooks firing on every Bash command invocation (including Why This Approach: The matcher pattern Risk Assessment: Low risk. This is a pure optimization that narrows trigger scope without changing hook behavior or removing any safety checks. Backlog Compliance
Technical Analysis✅ Excellent Changes
✅ Hook System UnderstandingThe changes correctly target these git-specific hooks:
While preserving non-git hooks:
✅ Performance ImpactThis optimization will significantly reduce unnecessary hook executions during common development tasks:
Minor Observations
Final Recommendation
Recommended merge: This PR delivers immediate token consumption benefits for Claude Code users with zero downside risk. |
Greptile SummaryThis PR attempted to reduce token consumption by narrowing Bash hook matchers from Confidence Score: 5/5Safe to merge — the revert restores both settings files to their correct base-branch state with no functional delta. All previously identified P0/P1 issues have been addressed by the final revert commit. The PR is a net-zero change from main, leaving hooks fully operational. No new issues were found. No files require special attention. Important Files Changed
Reviews (2): Last reviewed commit: "fix(hooks): revert Bash matchers to plai..." | Re-trigger Greptile |
.claude/settings.json
Outdated
| "PreToolUse": [ | ||
| { | ||
| "matcher": "Bash", | ||
| "matcher": "Bash(git )", |
There was a problem hiding this comment.
Hook matcher syntax is incorrect for the hooks system
The Claude Code hooks documentation is explicit: for PreToolUse and PostToolUse events, the matcher is a regex string matched against the tool name. The tool name for any Bash invocation is the plain string "Bash".
The regex Bash(git ) — when applied to the tool name "Bash" — will never match, because the pattern requires the string to be "Bashgit " (i.e., "Bash" immediately followed by "git "). With no match, none of these hooks will fire at all, silently disabling guard-git.sh, check-readme.sh, guard-pr-body.sh, pre-commit.sh, and lint-staged.sh for every git operation.
The Bash(command_prefix) parenthesis syntax is specific to Claude Code's permissions system (e.g. allowedTools: ["Bash(git commit:*)"]) and is not documented as a valid hook matcher pattern. The hooks reference examples show only bare tool names like "Bash", "Edit|Write", and "mcp__.*" as valid matchers for tool events.
The hooks already perform their own internal command filtering — guard-git.sh checks for (git|gh)\s+ on line 25, and pre-commit.sh checks for git\s+commit on line 26 — so keeping the matcher as "Bash" achieves correctness while the scripts themselves handle the narrowing.
| "matcher": "Bash(git )", | |
| "matcher": "Bash", |
The same issue affects the PostToolUse matcher on line 72 of this file.
There was a problem hiding this comment.
Fixed — reverted all matchers to plain "Bash". The hook scripts already filter internally for git commands (e.g. guard-git.sh checks for (git|gh)\s+, pre-commit.sh checks for git\s+commit), so narrowing at the matcher level is unnecessary and, as you correctly identified, breaks the hooks entirely since the matcher regex is applied to the tool name, not the command string.
.claude/settings.json
Outdated
| }, | ||
| { | ||
| "matcher": "Bash", | ||
| "matcher": "Bash(git )", |
There was a problem hiding this comment.
There was a problem hiding this comment.
Fixed — same revert applied to the PostToolUse matcher. Both PreToolUse and PostToolUse matchers in .claude/settings.json are now plain "Bash" again.
| }, | ||
| { | ||
| "matcher": "Bash", | ||
| "matcher": "Bash(git )", |
There was a problem hiding this comment.
Same invalid matcher pattern in the example docs
This file has the same "Bash(git )" matcher on lines 15 and 52. Both should revert to "Bash" for the same reasons: the hook matcher regex is applied to the tool name, not the command string, and Bash(git ) does not match "Bash".
| "matcher": "Bash(git )", | |
| "matcher": "Bash", |
Line 52 in this file has the same issue.
There was a problem hiding this comment.
Fixed — same revert applied to the docs example file (docs/examples/claude-code-hooks/settings.json). Both PreToolUse and PostToolUse matchers there are now plain "Bash" as well.
The `Bash(git )` matcher syntax does not work — the matcher regex is
applied to the tool name string ("Bash"), not the command argument.
Using `Bash(git )` silently disables all git safety hooks. The hook
scripts already filter internally for git commands, so narrowing at
the matcher level is unnecessary.
Summary
"Bash"to"Bash(git )"in both.claude/settings.jsonanddocs/examples/claude-code-hooks/settings.jsonls,npm test,node, etc.)Test plan
git commitfires pre-commit, lint-staged, guard-git)ls,npm test) no longer trigger git hooks