Ask HN: Simple tooling for local LLM code critique without IDE integration?

Several command-line tools enable local LLM code critique without IDE integration. Aider, LLMling, and fabric offer straightforward solutions that work directly from your terminal. These tools let you pipe code to local models like Llama or Mistral for instant feedback, avoiding complex plugin configurations while maintaining full control over your development workflow and data privacy.
You want code reviews from a local LLM without wrestling with IDE plugins, language servers, or configuration hell. You’re not alone. The good news: you can set this up in under ten minutes using tools that exist right now.
The Manual Method That Actually Works
The simplest approach uses your existing local LLM and a few shell commands. If you’re running Ollama, LM Studio, or llama.cpp, you already have everything you need.
Basic workflow:
# capture your code
cat src/my_file.py | pbcopy # macOS
cat src/my_file.py | xclip -selection clipboard # Linux
# Then paste into your LLM interface and ask for critique
This works, but it’s tedious. Let’s make it better.
Shell Script Approach
Create a simple script that sends code directly to your local LLM:
#!/bin/bash
# save as code-review.sh
FILE=$1
PROMPT="Review this code for bugs, performance issues, and maintainability. Be specific and actionable:nn$(cat $FILE)"
curl http://localhost:11434/api/generate -d "{
"model": "codellama",
"prompt": "$PROMPT",
"stream": false
}" | jq -r '.response'
Make it executable (chmod +x code-review.sh) and run it:
./code-review.sh src/authentication.js
This assumes Ollama running locally. For LM Studio, change the endpoint to http://localhost:1234/v1/chat/completions and adjust the JSON structure to match OpenAI’s format.
Directory-Level Reviews
Reviewing a single file helps, but what about entire modules? This script walks a directory and generates a consolidated review:
#!/bin/bash
# save as review-dir.sh
DIR=$1
OUTPUT="code_review_$(date +%Y%m%d_%H%M%S).md"
echo "# Code Review: $DIR" > $OUTPUT
echo "Generated: $(date)" >> $OUTPUT
echo "" >> $OUTPUT
find $DIR -name "*.py" -o -name "*.js" -o -name "*.go" | while read file; do
echo "## $file" >> $OUTPUT
echo '```' >> $OUTPUT
cat "$file" >> $OUTPUT
echo '```' >> $OUTPUT
REVIEW=$(curl -s http://localhost:11434/api/generate -d "{
"model": "codellama",
"prompt": "Review this code briefly. Focus on critical issues:nn$(cat $file)",
"stream": false
}" | jq -r '.response')
echo "$REVIEW" >> $OUTPUT
echo "" >> $OUTPUT
done
echo "Review saved to $OUTPUT"
Run it with ./review-dir.sh src/auth and you get a Markdown file with all your code and corresponding critiques.
Using Existing CLI Tools
Several command-line tools already exist for this exact purpose.
llm by Simon Willison is a Python tool that works with any OpenAI-compatible API:
pip install llm
# Configure for local endpoint
llm keys set local
# Enter: http://localhost:11434 (or your LM Studio port)
# Review a file
cat mycode.py | llm -m codellama "Review this code for issues"
# Save common prompts as templates
llm templates edit code-review
# Paste your preferred review prompt
# Then use it
cat mycode.py | llm -t code-review
aichat is another solid option written in Rust:
# Install
cargo install aichat
# Configure for Ollama
aichat --model ollama:codellama
# Review code
aichat --file mycode.js "Critique this code"
# Or use it interactively
aichat --role code-reviewer --file mycode.js
Both tools support conversation history, so you can ask follow-up questions about the critique.
Git Integration for Pre-Commit Reviews
You can hook this into your git workflow to review changes before committing:
#!/bin/bash
# .git/hooks/pre-commit
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '.(js|py|go)$')
if [ -z "$STAGED_FILES" ]; then
exit 0
fi
echo "Running AI code review on staged files..."
for FILE in $STAGED_FILES; do
echo "Reviewing $FILE..."
REVIEW=$(cat "$FILE" | llm -m codellama "Quick review: any critical bugs or security issues?")
if echo "$REVIEW" | grep -qi "critical|security|vulnerability"; then
echo "⚠️ Potential issues found in $FILE:"
echo "$REVIEW"
echo ""
read -p "Continue with commit? (y/n) " -n 1 -r
echo
if [[ ! $REPLYY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
done
This won’t stop you from committing, but it surfaces issues before they hit your repository.
Diff-Based Reviews
Instead of reviewing entire files, review just what changed:
# Review unstaged changes
git diff | llm -m codellama "Review these code changes"
# Review changes between branches
git diff main..feature-branch | llm -m codellama "Critique these changes"
# Review a specific commit
git show abc123 | llm -m codellama "Review this commit"
This focuses the LLM on recent modifications, which is often more useful than reviewing entire files.
Batch Processing with Parallel
If you’re reviewing many files, GNU parallel speeds things up:
find src -name "*.py" | parallel -j4 'echo "=== {} ===" && cat {} | llm -m codellama "Brief code review"'
This runs four reviews simultaneously. Adjust -j4 based on your CPU and RAM.
Structured Output with jq
For machine-readable reviews (useful for dashboards or further processing):
cat mycode.py | llm -m codellama "Review this code. Output JSON with fields: issues (array), severity (1-5), summary" | jq '.'
Some models follow JSON output instructions better than others. CodeLlama and Deepseek Coder work well for this.
Text Editor Integration (Without Full IDE)
If you use Vim, Emacs, or Sublime Text, you can add simple commands without heavy plugins.
Vim example:
" Add to .vimrc
command! -range CodeReview :<line1>,<line2>w !llm -m codellama "Review this code"
" Usage: visually select code, then :CodeReview
Emacs example:
;; Add to init.el
(defun code-review-region (start end)
(interactive "r")
(shell-command-on-region start end "llm -m codellama 'Review this code'" "*Code Review*"))
;; Bind to key: M-x code-review-region
These run in seconds and display results in a buffer.
Watch Mode for Active development
Use entr to automatically review files as you save them:
# Install entr (available in most package managers)
brew install entr # macOS
apt install entr # Ubuntu
# Watch and review
ls src/*.py | entr sh -c 'clear && cat src/main.py | llm -m codellama "Quick review"'
Every time you save main.py, you get a fresh review in your terminal.
We’re building something that does this automatically — join the notify list and we’ll tell you the day it’s ready.
.mai-waitlist{max-width:560px;margin:28px auto;padding:24px 24px 22px;border:1.5px solid #2f6bd8 !important;border-radius:14px;background:#0f2238 !important;box-shadow:0 10px 34px rgba(0,0,0,.40);box-sizing:border-box} .mai-waitlist *{box-sizing:border-box;font-family:inherit} .mai-waitlist .maiwl-h{margin:0 0 6px !important;padding:0 !important;font-weight:700 !important;font-size:1.18rem !important;color:#ffffff !important;line-height:1.35 !important} .mai-waitlist .maiwl-sub{margin:0 0 16px !important;padding:0 !important;font-size:.92rem !important;color:#a9bdd6 !important;line-height:1.5 !important} .mai-waitlist input,.mai-waitlist textarea{width:100% !important;padding:13px 14px !important;margin:0 0 10px !important;border:1px solid #345782 !important;border-radius:9px !important;font-size:16px !important;min-height:46px !important;background:#091a2c !important;color:#eef5fc !important;display:block !important} .mai-waitlist textarea{min-height:66px !important;resize:vertical !important} .mai-waitlist input:focus,.mai-waitlist textarea:focus{outline:none !important;border-color:#5b9dff !important;box-shadow:0 0 0 3px rgba(47,107,216,.25) !important} .mai-waitlist input::placeholder,.mai-waitlist textarea::placeholder{color:#7e96b3 !important;opacity:1 !important} .mai-waitlist .maiwl-hp{position:absolute !important;left:-9999px !important;width:1px !important;height:1px !important;min-height:0 !important;opacity:0 !important;margin:0 !important;padding:0 !important} .mai-waitlist .maiwl-btn{width:100% !important;padding:14px 18px !important;margin:2px 0 0 !important;border:0 !important;border-radius:9px !important;background:#2f6bd8 !important;color:#ffffff !important;font-size:16px !important;font-weight:700 !important;cursor:pointer !important;min-height:48px !important;box-shadow:0 5px 16px rgba(47,107,216,.45) !important;transition:background .15s} .mai-waitlist .maiwl-btn:hover{background:#3f7bf0 !important} .mai-waitlist .maiwl-msg{margin:12px 0 0 !important;padding:0 !important;font-size:.92rem !important;color:#a9bdd6 !important;min-height:1em !important}🚀 Want this as an automatic tool? Join the notify list.
We’ll email you the day it’s ready — no spam, just the launch.
Notify me at launch
function maiWaitlist(btn){ var box = btn.closest('.mai-waitlist'); var name = box.querySelector('.maiwl-name').value.trim(); var email = box.querySelector('.maiwl-email').value.trim(); var wants = box.querySelector('.maiwl-wants').value.trim(); var hp = box.querySelector('.maiwl-hp').value; var app = box.querySelector('.maiwl-app').value; var nonce = box.querySelector('.maiwl-nonce').value; var post = box.querySelector('.maiwl-post').value; var msg = box.querySelector('.maiwl-msg'); function setMsg(c,t){ msg.style.setProperty('color',c,'important'); msg.textContent=t; } if(!name){ setMsg('#ff8a8a','Please enter your name.'); return; } if(!email || email.indexOf('@')<1){ setMsg('#ff8a8a','Please enter a valid email.'); return; } btn.disabled = true; var label = btn.textContent; btn.textContent = 'Sending…'; var params = new URLSearchParams(); params.append('name', name); params.append('email', email); params.append('wants', wants); params.append('hp', hp); params.append('app_slug', app); params.append('nonce', nonce); params.append('source_post_id', post); params.append('utm', (window.location.search||'').replace(/^?/,'')); fetch('https://blog.masterailabs.com/wp-json/mai/v1/waitlist', { method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded'}, body: params.toString() }).then(function(r){ return r.json(); }).then(function(d){ btn.textContent = label; btn.disabled = false; if(d && d.ok){ setMsg('#5fe39b', d.message || 'You're in — check your email to confirm.'); box.querySelector('.maiwl-name').value=''; box.querySelector('.maiwl-email').value=''; box.querySelector('.maiwl-wants').value=''; } else { setMsg('#ff8a8a', (d && d.message) || 'Something went wrong. Try again.'); } }).catch(function(){ btn.textContent = label; btn.disabled = false; setMsg('#ff8a8a','Network error. Try again.'); }); }
FAQ
Q: Which local model works best for code review?
CodeLlama (13B or 34B), Deepseek Coder (6.7B or 33B), and Phind CodeLlama are the strongest options. The 13B models run on 16GB RAM; 34B models need 32GB or quantization. WizardCoder and StarCoder2 are decent alternatives.
Q: how do i make reviews more actionable?
Be specific in your prompts. Instead of “review this code,” try “identify security vulnerabilities, suggest performance improvements, and flag unclear variable names.” Include your language and framework in the prompt for context-aware feedback.
Q: Can I review code in languages the model wasn’t specifically trained on?
Yes, but quality varies. Models trained on broad code datasets (like CodeLlama) handle most mainstream languages reasonably well. Niche languages or DSLs get weaker reviews. Always verify suggestions.
Q: How long does a typical review take?
On a modern CPU, reviewing a 200-line file takes 10-30 seconds with a 13B model, 30-90 seconds with a 34B model. GPU inference is 3-5x faster. Diff-based reviews are quicker since they process less text.
Q: What if I want to save and compare reviews over time?
Append reviews to dated files (review_YYYYMMDD.md) or commit them to a separate branch. You can also pipe output to SQLite for queryable history: cat file.py | llm -m codellama "review" | sqlite3 reviews.db "INSERT INTO reviews VALUES (datetime('now'), 'file.py', stdin())" with appropriate schema.
Our AI Tools
See all our apps →📚 Free: Get Found by AI — the 2026 GEO Playbook
Get the free ebook on how to get your brand cited by ChatGPT, Claude, Gemini & Perplexity — plus new posts as we publish them.
No spam. Unsubscribe anytime in one click.
