Posted on

The QA Assistant on demand.

Thoughts of the Fractional ChiefUsing a Local AI Code Reviewer in a Git Workflow

I recently wrote about the laminar team and this is a bit of a follow-up on the QA part and AI assist in rapid QA as well as for use as support on command line. 

The idea is simple: take one or more source files, apply a configurable instruction set, send the contents to a local Ollama model, and return the review either by email or directly in the terminal.

The useful part is not the model call itself. It is how the tool fits into a wider agentic workflow, particularly when combined with Git hooks.

The basic flow

The application is deliberately independent of Git. It accepts files as command-line parameters:

rapidqa main.go server.go web/app.js

For each file, it checks the extension against a configured allow-list, ignores binary content, reads the source, prepends the configured instructions, and sends the combined prompt to a locally running Ollama instance.

Each response is collected under a clear filename delimiter so multiple files can be reviewed in a single run.

By default, the combined result is sent through SendGrid as a single email.

This keeps the application generic. Git is only one possible way to invoke it.

Local review with -nomail

For development and ad-hoc use, sending an email every time is unnecessary, so I added a -nomail option:

rapidqa -nomail main.go

Instead of using SendGrid, the generated report is printed directly to the console.

This makes the same application useful both as an automated review service and as a normal command-line tool.

For example:

rapidqa -nomail main.go handlers.go database.go

can be used while working on a change without involving Git at all.

Overriding the instructions

The application also supports -in, which allows a different instruction to be supplied for a particular run.

The default behaviour uses the global instruction file stored alongside the application:

rapidqa/ rapidqa config/ config.json instructions.txt 

That instruction might contain a general code-review prompt covering areas such as correctness, security, error handling and unnecessary complexity.

For a specific review, however, the instruction can be replaced from the command line:

rapidqa -nomail -in "Look only for concurrency and locking problems" server.go

This turns the utility into a more general local analysis tool.

The same model can therefore be used for several tasks without changing the source code or global configuration.

Examples might include:

rapidqa -nomail -in "Check this code for SQL injection risks" database.go

or:

rapidqa -nomail -in "Identify duplicated logic and suggest where functions should be extracted" handlers.go routes.go

Adding Git to the workflow

Because the application accepts normal filenames, Git integration can remain outside the application itself.

A global post-commit hook can obtain the files included in the most recent commit and pass them directly to commit-review.

A simplified version looks like this:

#!/usr/bin/env bash

REPO="$(git rev-parse --show-toplevel)"
cd "$REPO" || exit 1

files=()

if git rev-parse --verify HEAD^ >/dev/null 2>&1; then
    mapfile -d '' files < <(
        git diff --name-only --diff-filter=ACMRT -z HEAD^ HEAD
    )
else
    mapfile -d '' files < <(
        git diff-tree --root --no-commit-id --name-only --diff-filter=ACMRT -r -z HEAD
    )
fi

[ "${#files[@]}" -eq 0 ] && exit 0

absolute_files=()

for file in "${files[@]}"; do
    absolute_files+=("$REPO/$file")
done

/opt/rapidqa/rapidqa -- "${absolute_files[@]}"

The hook does the Git-specific work. It determines which files changed and passes them to the application.

The Go program itself does not need to know anything about repositories, branches, commits or Git internals.

That separation is useful because the same binary can still be invoked manually, from a build script, from CI, or from another automation system.

Making the hook global

The hook itself can also be global rather than copied into every repository.

Git supports a global hooks directory:

git config --global core.hooksPath ~/.git-hooks

The resulting setup is roughly:

~/.git-hooks/
    post-commit

/opt/rapidqa/ rapidqa config/ config.json instructions.txt 

Every repository using that Git configuration now runs the same review process after a successful commit.

There is no need to add review scripts, configuration files or binaries to individual projects.

Why use a local model?

Using Ollama means the source does not need to be sent to a remote model provider.

The application reads the source locally, combines it with the instructions, and sends it to the configured Ollama API endpoint, usually something such as:

http://127.0.0.1:11434

The model itself is defined in config.json, along with settings such as timeout, context size and temperature.

This also means the review process can be adapted to the available hardware. A smaller model can be used on a workstation, while a larger coding model can be used on a dedicated local machine.

From automation to an agentic workflow

The interesting part of this approach is that Git becomes the event source rather than the centre of the application.

A commit occurs, the hook identifies the relevant files, and the review agent is given:

  • a set of files
  • a defined instruction
  • a local model
  • an output destination

The agent performs the review and returns the result without requiring another manual step.

At the same time, the command-line interface remains available:

commit-review -nomail -in "Review error handling only" *.go

That makes the same application usable interactively as well as automatically.

The distinction is useful. The tool is not a “Git AI reviewer”. It is a file-processing review agent that Git happens to invoke automatically.

Keeping the boundaries simple

The implementation has ended up with a fairly clean division of responsibility.

  • Git decides which files belong to the event.
  • The Go application decides which files are suitable for processing, reads them, builds the prompts, calls Ollama and formats the results.
  • The configuration controls the model, accepted file extensions and email settings.
  • The instruction file controls what the reviewer is supposed to look for.
  • SendGrid provides asynchronous delivery, while -nomail provides immediate terminal output.
  • The -in option allows the behaviour of the reviewer to be changed for individual runs without changing the global defaults.
  • That separation keeps the application small while making it useful in more situations than the original post-commit use case.

Git repo: 

https://github.com/emberlabstech/RapidQA

License: MIT