Building a Local AI Code Reviewer with Ollama That Catches Bugs Before Your Team A developer built a local AI code reviewer using Ollama and the qwen2.5-coder:7b model that runs as a pre-commit hook. The TypeScript CLI feeds staged git diffs to the LLM and returns structured bug findings without sending code to the cloud. The tool focuses on logic errors, null access, resource leaks, and security issues while ignoring style and formatting suggestions. Your teammates are busy. Your CI is green but shallow. And the bug you just staged is the kind a second pair of eyes would catch in five seconds. So let's build that second pair of eyes: a small TypeScript CLI that feeds your staged git diff to a local LLM and returns structured findings, before anyone else sees your code. No API key, no cloud, no leaking your private repo to a vendor. The whole tool is one loop: git diff --cached . pre-commit hook.Everything runs locally against qwen2.5-coder:7b . You'll need Ollama running ollama serve and the model pulled ollama pull qwen2.5-coder:7b . The reviewer should look at exactly what you're about to commit, nothing more. That's --cached staged changes only : js import { execSync } from "node:child process"; function getStagedDiff : string { return execSync "git diff --cached --no-color -U3", { encoding: "utf8", maxBuffer: 10 1024 1024, } ; } A few choices that matter: --no-color keeps ANSI escape codes out of the prompt. -U3 gives three lines of context around each hunk. Enough for the model to reason, not so much that you blow the context window. maxBuffer bumps Node's default 1MB cap so big diffs don't throw.If the diff is empty, there's nothing to review: js const diff = getStagedDiff ; if diff.trim .length === 0 { console.log "No staged changes. Stage something first with git add ." ; process.exit 0 ; } This is where the quality lives. A vague prompt gives you vague, hallucinated nitpicks. Be specific about what counts as a finding, and what to ignore. js const SYSTEM PROMPT = You are a senior code reviewer. You review git diffs for bugs only. Focus on: - Logic errors off-by-one, inverted conditions, wrong operators - Null/undefined access and unhandled error cases - Resource leaks unclosed handles, missing awaits - Security issues injection, hardcoded secrets, unsafe input Do NOT report: - Style, formatting, or naming preferences - Suggestions to add comments or tests - Anything you are not confident is an actual bug Lines starting with "+" are added. Lines starting with "-" are removed. Only review added "+" lines. Respond with ONLY valid JSON. ; The "do NOT report" block is doing heavy lifting. Small models love to pad output with "consider adding a comment here." Telling them what to suppress is more effective than telling them what to find. The instruction to only review + lines matters too. Without it, the model will happily flag a bug in code you just deleted, which is both useless and confusing. Diffs are a strange dialect to a model trained mostly on whole files, so being explicit about what the + and - prefixes mean pays off in fewer nonsense findings. Ollama speaks the OpenAI-compatible API at localhost:11434 . Spell out the exact schema in the prompt and set temperature: 0 so the output is deterministic: js const RESPONSE SCHEMA = Respond with this exact JSON shape: { "findings": { "severity": "high" | "medium" | "low", "file": "string", "line": "string the code snippet or line reference ", "issue": "string one sentence: what is wrong ", "fix": "string one sentence: how to fix it " } } If there are no bugs, return { "findings": }. ; async function reviewDiff diff: string, model: string : Promise