Claude God Tip #2: Turn Git Hooks Into AI-Powered Code Reviewers
Published: November 18, 2025 • 8 min read
The beautiful thing about attempting to be a Claude God is that I'm aiming for a level of mastery where no stones must be left unturned. And I mean none, nada. In that process, I am going to slowly but surely become less of a chauffeur when working with Claude and other adjoining tools that I use Claude with.
In the blog post where I launched this series, I mentioned that I'll either give one big tip or one small tip. Well today, we're looking at a big tip.
Before you attempt to follow along with this, if you haven't installed Claude in your terminal yet, you should probably check out the quickstart guide to get started.
The Problem: Repetitive Commit Instructions
As I build applications using Claude in the terminal, I often want it to commit the changes we've made. But there are a series of actions I have to specify to Claude before asking it to commit changes. Having to specify these steps every single time I need Claude to commit multiple batches of changes can be time consuming.
Look at me, complaining about guiding an AI to make commits for me when I don't even have to do the work... how did I become this lazy?
But you see, laziness does not always have to be a bad thing. When you combine selective laziness with a deep hatred of mediocrity (and therefore high standards for the work you create), you seek out ways to improve processes that most people don't even think of.
So today, we are going to be talking about combining Git Hooks with the amazing power of Claude.
What Are Git Hooks?
If you are reading this, you have probably worked with Git and know a number of Git commands, the most popular being git add, git commit, git push, git pull, etc. What if I tell you that you can create scripts that run automatically before any of these commands fully execute?
Git hooks are like bodyguards to specific actions in your Git repository. These bodyguards exist in the form of scripts which, when used to their full capacity, will allow or deny a specific Git command from executing.
You can also think about them as "If this, then that" rules for Git:
- IF you're about to commit code → THEN run this check first
- IF you're about to push code → THEN run tests first
- IF you just pulled code → THEN install new dependencies
Types of Git Hooks
There are many types of hooks. Here are some of the most popular ones:
pre-commit: This hook runs before a commit is created to check code quality and run linters. It's a bodyguard of the git commit command.
prepare-commit-msg: This hook runs before a commit message editor opens to generate commit messages. It's also a bodyguard of the git commit command.
commit-msg: This hook runs after you write a commit message to validate the format of the message. It's also a bodyguard of the git commit command. When you think about it, it makes sense that this command needs multiple bodyguards.
pre-push: This hook runs before you push a repository to the remote branch to run tests and prevent one from pushing broken code. It's a bodyguard of the git push command.
post-commit: This hook runs after a commit is created to possibly send notifications and update any logs. In this instance, you can see that this specific bodyguard of the git commit command does a really good job at being a messenger as well.
Where Do Hooks Live?
Now you may be wondering, where do these hooks (which exist as scripts) live?
Good question! You know that .git folder that is created every time you initialize a Git repository in a directory? Yup, that one. That is where the hook scripts live. You create a script file for any hook you want in the .git/hooks/ directory.
For this blog post, we are going to focus on the pre-commit hook.
The Traditional Way: Pre-Commit Without AI
Now how did our ancestors use the pre-commit hook? Great question again! You've been asking a lot of good questions, keep it up!
When I refer to "our ancestors," I am talking about every single one of us programmers (myself included), but specifically about what our workflow with the pre-commit hook looked like without AI integration.
The pre-commit hook below will check for any console.log statements in our staged files. This is an important check for most developers as we do not want sensitive data that was logged to the console during testing to be pushed to production. I have been guilty of this in the past as it can be easy to forget, but when you have a bodyguard in front of the git commit command, it leaves no room for being careless.
Step 1: Create the Hook Script
Navigate to your project and create the script using this command:
nano .git/hooks/pre-commit
Step 2: Add the Script Contents
In the nano editor, add the following:
#!/bin/bash
# .git/hooks/pre-commit
echo "🔍 Checking for console.logs..."
# Get list of staged .js and .ts files
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|tsx)$')
# Check each file for console.log
if echo "$FILES" | xargs grep -n "console.log" 2>/dev/null; then
echo ""
echo "❌ Found console.log statements!"
echo "Remove them before committing."
exit 1 # Exit with failure (1) to prevent commit
fi
echo "✅ No console.logs found."
exit 0 # Exit with success (0) to allow commit
Save and exit nano by:
- Pressing
Ctrl + X - Pressing
Y(yes, save) - Pressing
Enter(to confirm the filename)
Step 3: Make the Script Executable
Run the command:
chmod +x .git/hooks/pre-commit
This command does the following:
chmod= "change mode" (changes the file permissions)+x= "add the execute permission"
Without this step, Git cannot run the script.
Understanding the Script
Let me break down the key parts:
#!/bin/bash: This line tells the system that this is a bash script. If you do not add this line, Git will not know how to run the file.
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|tsx)$'): This command gets a list of staged files that end with .js, .ts, or .tsx.
Here's what each part does:
git diff --cached= show changes in staging area--name-only= only show filenames, not content--diff-filter=ACM= Added, Copied, or Modified files (not Deleted)grep -E '\.(js|ts|tsx)$'= grab files that end with .js, .ts, or .tsx
The if statement: The part of the code surrounded by if and fi checks each file for any console.log() statements. If there are any, the exit 1 command will prevent the commit from happening. If there are none, the exit 0 command will allow the commit to proceed.
Now when you complete the steps above, you can run Git commands as normal using git commit -m 'Commit message' and watch the bodyguard do its thing.
The Problem with Traditional Hooks
Here's the thing. This bodyguard we just created, by relying solely on the ancient powers of grep, isn't very smart. (Sorry grep! I do not deny your usefulness, I promise.)
The script above simply checks for the text console.log. But what if the statement was in a comment? What if it's a harmless console.log statement that poses no risks? What if it's in a file you intentionally want to keep for debugging?
You see, grep does not understand context. And that is why we, the developers of today, must embrace the more enhanced version of checking intelligently with Claude.
The Claude-Powered Way: Intelligent Pre-Commit Checks
The only difference between this version and the one above is the content of the script. Replace your pre-commit hook with this:
#!/bin/bash
# .git/hooks/pre-commit
echo "🔍 Running code quality checks with Claude..."
# Get staged files
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
# Ask Claude to review
REVIEW_RESULT=$(claude "
Review these staged files for commit:
Files: $STAGED_FILES
Check for:
1. console.log statements that should be removed
2. TODO comments that need addressing
3. Missing TypeScript types on functions
4. Obvious code quality issues
Rules:
- Ignore files in: node_modules/, dist/, build/, .next/
- Only check staged files (files about to be committed)
- For each issue found:
* Show filename and line number
* Explain why it's a problem
* Suggest how to fix it
If NO issues found, output: 'CHECKS_PASSED'
If issues found, list them clearly.
")
# Check result
if echo "$REVIEW_RESULT" | grep -q "CHECKS_PASSED"; then
echo "✅ All checks passed!"
exit 0
else
echo ""
echo "❌ Issues found:"
echo "$REVIEW_RESULT"
echo ""
read -p "Fix issues now? (y=fix and stop commit, n=commit anyway) " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Nn]$ ]]; then
echo "⚠️ Proceeding with commit despite issues..."
exit 0
else
echo "🛠️ Please fix the issues and try committing again."
exit 1
fi
fi
Understanding the Claude-Powered Script
Let me break down the parts that are different from the traditional version:
REVIEW_RESULT=$(claude "..."): This runs the Claude command and captures the output in a variable:
$(...)= "run this command and save the output"- Output gets stored in the
REVIEW_RESULTvariable
if echo "$REVIEW_RESULT" | grep -q "CHECKS_PASSED"; then: This checks if the output returned by Claude contains "CHECKS_PASSED". See, grep is still useful here! We must never completely abandon our ancient ways.
echo "$REVIEW_RESULT"= print the variable|= pipe (send output to next command)grep -q "CHECKS_PASSED"= quietly search for this textif ... then= if found, do this
read -p "Fix issues now? (y/n) " -n 1 -r: This asks the user for input to determine the next steps:
"..."= prompt message-n 1= read only 1 character (don't wait for Enter)-r= raw input (don't interpret backslashes)
if [[ $REPLY =~ ^[Nn]$ ]]; then: This checks if the user pressed 'n' or 'N':
$REPLY= variable containing user's input=~= matches regex pattern^[Nn]$= exactly 'n' or 'N', nothing else
Why This Approach Is Better
What makes this approach better? Well, a lot of things actually!
First, Claude attempts to understand the code context. It's not just reading lines, it's analyzing them. It explains the issue in natural language and can even suggest fixes. It also gives you a choice to fix the issue or proceed with the commit, allowing for more flexibility.
It's just awesome, isn't it?
And the script above is not just checking for console.log statements. It checks for:
console.logstatements that should be removed- TODO comments that need addressing
- Missing TypeScript types on functions
- Obvious code quality issues
Expanding Your Checks
You can expand this list further by asking Claude to check for as many things as you'd like that might be relevant to your project or company. For instance, it could check for:
- Hardcoded API keys or secrets (security vulnerability)
- Commented-out code blocks (code cleanliness)
- Functions longer than 50 lines (maintainability)
- Missing error handling in async functions (reliability)
- Unused imports (bundle size optimization)
- Accessibility issues in JSX (a11y compliance)
- SQL queries without parameterization (SQL injection prevention)
- Missing unit tests for new functions (test coverage)
- Deprecated API usage (future-proofing)
- Non-descriptive variable names (code readability)
Just add them to the "Check for:" section of your prompt!
Bypassing the Hook (When Necessary)
One final note. If you ever need to bypass the rules you set for the pre-commit hook, you can always commit with the --no-verify flag:
git commit --no-verify -m "emergency fix"
Use this sparingly! The whole point of the hook is to catch issues before they make it into your codebase. But sometimes you need to push an emergency fix and deal with the cleanup later.
And that's all for Claude God Tip #2! This one was a big tip, but I hope it helps you level up your Git workflow with AI-powered code reviews.
As always, thanks for reading!