Tools: How to Automate Code Reviews with Local LLMs (No API Keys Required)

Tools: How to Automate Code Reviews with Local LLMs (No API Keys Required)

Why Local LLMs for Code Review?

What You'll Need

Step 1: Install Ollama

Step 2: Create the Review Script

Step 3: Set Up the Git Hook

Step 4: Test It

Making It Actually Useful

Skip Reviews for Trivial Commits

Focus on Specific File Types

Bypass When Needed

Log Reviews for Later

Performance Notes

What It Won't Catch

The Actual Value I got tired of waiting for PR reviews. My team's across three timezones, and sometimes a simple "is this logic right?" question sits for 12 hours. So I built an automated pre-commit code review using Ollama and git hooks. It runs entirely localβ€”no API keys, no usage limits, no sending proprietary code to external servers. Here's the setup that's been running on my machine for two months. Cloud APIs are great until: Running local LLMs for coding tasks solves all of this. The quality isn't GPT-4, but for catching obvious bugs and suggesting improvements? It's surprisingly good. Pull a coding-focused model. I've tested several; here's what works: Save this as ~/.local/bin/ai-review: Create a pre-commit hook in your repo: Want this globally? Use git templates: Stage some code and commit: You'll see something like: The basic setup works, but here's how I've tuned mine: The 6.7B model catches 80% of what the larger models find. For pre-commit automation, speed matters more than catching edge cases. Be realistic. Local LLMs miss: This isn't a replacement for human review. It's a first pass that catches the embarrassing stuff before your teammates see it. Two months in, here's what I've noticed: The whole setup took 10 minutes. The ROI has been significant. More at dev.to/cumulus Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

# Linux/WSL -weight: 500;">curl -fsSL https://ollama.com/-weight: 500;">install.sh | sh # macOS -weight: 500;">brew -weight: 500;">install ollama # Start the -weight: 500;">service ollama serve # Linux/WSL -weight: 500;">curl -fsSL https://ollama.com/-weight: 500;">install.sh | sh # macOS -weight: 500;">brew -weight: 500;">install ollama # Start the -weight: 500;">service ollama serve # Linux/WSL -weight: 500;">curl -fsSL https://ollama.com/-weight: 500;">install.sh | sh # macOS -weight: 500;">brew -weight: 500;">install ollama # Start the -weight: 500;">service ollama serve # Best balance of speed and quality ollama pull deepseek-coder:6.7b # If you have 16GB+ VRAM ollama pull codellama:13b # Best balance of speed and quality ollama pull deepseek-coder:6.7b # If you have 16GB+ VRAM ollama pull codellama:13b # Best balance of speed and quality ollama pull deepseek-coder:6.7b # If you have 16GB+ VRAM ollama pull codellama:13b #!/bin/bash set -e MODEL="${AI_REVIEW_MODEL:-deepseek-coder:6.7b}" DIFF=$(-weight: 500;">git diff --cached --diff-filter=ACMR) if [ -z "$DIFF" ]; then echo "No staged changes to review" exit 0 fi PROMPT="Review this code diff. Be concise. Flag: 1. Obvious bugs or logic errors 2. Security issues (SQL injection, XSS, hardcoded secrets) 3. Performance problems 4. Missing error handling If the code looks fine, just say 'LGTM'. Diff: $DIFF" echo "πŸ” Running local code review..." echo "" ollama run "$MODEL" "$PROMPT" 2>/dev/null echo "" echo "---" echo "Review complete. Commit? [y/N]" read -r response if [[ "$response" =~ ^[Yy]$ ]]; then exit 0 else exit 1 fi #!/bin/bash set -e MODEL="${AI_REVIEW_MODEL:-deepseek-coder:6.7b}" DIFF=$(-weight: 500;">git diff --cached --diff-filter=ACMR) if [ -z "$DIFF" ]; then echo "No staged changes to review" exit 0 fi PROMPT="Review this code diff. Be concise. Flag: 1. Obvious bugs or logic errors 2. Security issues (SQL injection, XSS, hardcoded secrets) 3. Performance problems 4. Missing error handling If the code looks fine, just say 'LGTM'. Diff: $DIFF" echo "πŸ” Running local code review..." echo "" ollama run "$MODEL" "$PROMPT" 2>/dev/null echo "" echo "---" echo "Review complete. Commit? [y/N]" read -r response if [[ "$response" =~ ^[Yy]$ ]]; then exit 0 else exit 1 fi #!/bin/bash set -e MODEL="${AI_REVIEW_MODEL:-deepseek-coder:6.7b}" DIFF=$(-weight: 500;">git diff --cached --diff-filter=ACMR) if [ -z "$DIFF" ]; then echo "No staged changes to review" exit 0 fi PROMPT="Review this code diff. Be concise. Flag: 1. Obvious bugs or logic errors 2. Security issues (SQL injection, XSS, hardcoded secrets) 3. Performance problems 4. Missing error handling If the code looks fine, just say 'LGTM'. Diff: $DIFF" echo "πŸ” Running local code review..." echo "" ollama run "$MODEL" "$PROMPT" 2>/dev/null echo "" echo "---" echo "Review complete. Commit? [y/N]" read -r response if [[ "$response" =~ ^[Yy]$ ]]; then exit 0 else exit 1 fi chmod +x ~/.local/bin/ai-review chmod +x ~/.local/bin/ai-review chmod +x ~/.local/bin/ai-review # In your project directory cat > .-weight: 500;">git/hooks/pre-commit << 'EOF' #!/bin/bash ~/.local/bin/ai-review EOF chmod +x .-weight: 500;">git/hooks/pre-commit # In your project directory cat > .-weight: 500;">git/hooks/pre-commit << 'EOF' #!/bin/bash ~/.local/bin/ai-review EOF chmod +x .-weight: 500;">git/hooks/pre-commit # In your project directory cat > .-weight: 500;">git/hooks/pre-commit << 'EOF' #!/bin/bash ~/.local/bin/ai-review EOF chmod +x .-weight: 500;">git/hooks/pre-commit mkdir -p ~/.-weight: 500;">git-templates/hooks cp ~/.local/bin/ai-review ~/.-weight: 500;">git-templates/hooks/pre-commit -weight: 500;">git config --global init.templateDir ~/.-weight: 500;">git-templates mkdir -p ~/.-weight: 500;">git-templates/hooks cp ~/.local/bin/ai-review ~/.-weight: 500;">git-templates/hooks/pre-commit -weight: 500;">git config --global init.templateDir ~/.-weight: 500;">git-templates mkdir -p ~/.-weight: 500;">git-templates/hooks cp ~/.local/bin/ai-review ~/.-weight: 500;">git-templates/hooks/pre-commit -weight: 500;">git config --global init.templateDir ~/.-weight: 500;">git-templates -weight: 500;">git add suspicious-code.py -weight: 500;">git commit -m "add feature" -weight: 500;">git add suspicious-code.py -weight: 500;">git commit -m "add feature" -weight: 500;">git add suspicious-code.py -weight: 500;">git commit -m "add feature" πŸ” Running local code review... Issues found: 1. **SQL Injection** (line 23): User input passed directly to query. Use parameterized queries instead. 2. **Missing null check** (line 45): `user.profile` accessed without verifying user exists. 3. **Hardcoded credential** (line 12): API key in source code. Move to environment variable. --- Review complete. Commit? [y/N] πŸ” Running local code review... Issues found: 1. **SQL Injection** (line 23): User input passed directly to query. Use parameterized queries instead. 2. **Missing null check** (line 45): `user.profile` accessed without verifying user exists. 3. **Hardcoded credential** (line 12): API key in source code. Move to environment variable. --- Review complete. Commit? [y/N] πŸ” Running local code review... Issues found: 1. **SQL Injection** (line 23): User input passed directly to query. Use parameterized queries instead. 2. **Missing null check** (line 45): `user.profile` accessed without verifying user exists. 3. **Hardcoded credential** (line 12): API key in source code. Move to environment variable. --- Review complete. Commit? [y/N] # Add to the script, after getting DIFF LINES_CHANGED=$(echo "$DIFF" | grep -c "^+" || true) if [ "$LINES_CHANGED" -lt 5 ]; then echo "Small change, skipping review" exit 0 fi # Add to the script, after getting DIFF LINES_CHANGED=$(echo "$DIFF" | grep -c "^+" || true) if [ "$LINES_CHANGED" -lt 5 ]; then echo "Small change, skipping review" exit 0 fi # Add to the script, after getting DIFF LINES_CHANGED=$(echo "$DIFF" | grep -c "^+" || true) if [ "$LINES_CHANGED" -lt 5 ]; then echo "Small change, skipping review" exit 0 fi # Only review Python and JavaScript DIFF=$(-weight: 500;">git diff --cached --diff-filter=ACMR -- '*.py' '*.js' '*.ts') # Only review Python and JavaScript DIFF=$(-weight: 500;">git diff --cached --diff-filter=ACMR -- '*.py' '*.js' '*.ts') # Only review Python and JavaScript DIFF=$(-weight: 500;">git diff --cached --diff-filter=ACMR -- '*.py' '*.js' '*.ts') # Skip the hook for quick fixes -weight: 500;">git commit --no-verify -m "typo fix" # Skip the hook for quick fixes -weight: 500;">git commit --no-verify -m "typo fix" # Skip the hook for quick fixes -weight: 500;">git commit --no-verify -m "typo fix" # Append to script before the prompt REVIEW_LOG=~/.local/share/ai-reviews/$(date +%Y-%m-%d).log mkdir -p "$(dirname "$REVIEW_LOG")" echo "=== $(date) ===" >> "$REVIEW_LOG" echo "$DIFF" >> "$REVIEW_LOG" # Append to script before the prompt REVIEW_LOG=~/.local/share/ai-reviews/$(date +%Y-%m-%d).log mkdir -p "$(dirname "$REVIEW_LOG")" echo "=== $(date) ===" >> "$REVIEW_LOG" echo "$DIFF" >> "$REVIEW_LOG" # Append to script before the prompt REVIEW_LOG=~/.local/share/ai-reviews/$(date +%Y-%m-%d).log mkdir -p "$(dirname "$REVIEW_LOG")" echo "=== $(date) ===" >> "$REVIEW_LOG" echo "$DIFF" >> "$REVIEW_LOG" - You're working with sensitive code - You hit rate limits at 2 AM debugging - Your company's security policy says no external AI - You don't want to pay per token for every commit - Ollama - Dead simple local LLM runner - A decent GPU - 8GB VRAM minimum, 16GB recommended - Git - Obviously - 10 minutes - That's genuinely it - deepseek-coder:6.7b - ~3 seconds for typical diffs - codellama:13b - ~8 seconds, slightly better catches - codellama:34b - ~25 seconds, overkill for pre-commit - Complex architectural issues - Business logic errors (it doesn't know your domain) - Subtle race conditions - Whether your code actually solves the right problem - Fewer "oops" commits - It catches the dumb mistakes I make at midnight - Faster PR reviews - Human reviewers focus on architecture, not typos - Better habits - Knowing there's a check makes me write cleaner first drafts