Agentic pre-commit hook with Opencode Go SDK

5 min read Original article ↗

I’ve been an avid user of Opencode for a long time, even before it became widely popular. It’s not the only coding agent in my toolkit (I also reach for Amp from time to time), but Opencode holds a special place because of its LSP integration and the dead-simple ability to swap models on the fly.

It’s also remarkably extensible. You can write plugins in TypeScript, apply custom themes, build tools and web apps around it, and even extend it with Skills. The community around it is growing fast — check out awesome-opencode or opencode.cafe if you want to explore what people are building.

What I recently discovered, though, is that Opencode has a Go SDK (not to be confused with Opencode Go), and that’s what inspired this whole project. Let’s see what we can build with it.

I’m the type of developer who absolutely hates typos in code and stray debug print statements — and also the type who produces them constantly. Regular formatters and linters don’t always catch these things, and let’s be honest: other code reviewers aren’t exactly trustworthy either. Nobody really reads the code anymore.

There are tools like CodeRabbit, GitHub Copilot, and Graphite that can review your pull requests after the fact. But what if I want to run similar checks before committing my code? And what if I want those checks to be configurable?

The idea: run an AI-powered code review as a pre-commit hook, using a coding agent that’s already aware of the codebase.

Since I use Opencode daily, let’s build it with that.

Here’s something a lot of people don’t realize: Opencode isn’t simply a TUI wrapper. It ships with a full server, and the TUI is just one client that talks to it. That means you can connect to the server from anywhere — your phone, a web browser, or a custom Go program.

You can start the server standalone on any port, optionally password-protected:

This also means you can run multiple Opencode instances and agents simultaneously.

Here’s the high-level flow of what we’re building:

┌──────────────┐     ┌──────────────────┐     ┌────────────────┐
│  git commit  │────▶│  pre-commit hook │────▶│ Opencode Server│
│  (triggers)  │     │  (Go binary)     │     │                │
└──────────────┘     └──────────────────┘     └────────┬───────┘
                              │                         │
                              │  1. Get staged diff      │
                              │  2. Create session       │
                              │  3. Send diff + prompt   │
                              │  4. Parse JSON response  │
                              │  5. Pass / Fail commit   │
                              ▼                         ▼
                     ┌──────────────────┐     ┌────────────────┐
                     │  Terminal Output │     │   LLM (Opus)   │
                     │  issues / pass   │     │   via Opencode │
                     └──────────────────┘     └────────────────┘

The Opencode Go SDK wraps the standard REST API that the server exposes. For our pre-commit hook, we only need three operations:

SDK Method Purpose Session.New Create a fresh review session Session.Prompt Send the diff and get the review back Session.Delete Clean up the session when we’re done

That’s it. Minimal surface area for a focused tool.

The pre-commit hook ships with a default prompt that asks the LLM to review for typos, unnecessary debug statements, security issues, bugs, and code style violations — essentially a lightweight CodeRabbit that runs locally.

The tricky part is that LLMs answer in an unstructured way by default, which is hard to parse programmatically. So we need to instruct the model to return its answer as strict JSON and hope it actually follows those instructions.

Here’s the full default prompt, which includes the staged git diff:

The idea is that users can later customize this prompt per-repo to match their own conventions and priorities.

With the prompt ready, we make a sequence of SDK calls. The flow is straightforward — create a session, send the prompt, read the response, then clean up:

A one-minute timeout is more than enough for a typical diff review.

The response comes back as a JSON string (if the model cooperated), which we deserialize into simple Go structs:

Then we parse and display the results:

If the status is "fail", we exit with code 1, which tells Git to abort the commit. Simple and effective.

There are fancier ways to manage pre-commit hooks — frameworks like pre-commit or tools like Husky. But a pre-commit hook is really just an executable file, so manual installation is trivial.

Drop the following into .git/hooks/pre-commit:

Make sure the Go binary (opencode-pre-commit) is somewhere on your $PATH, and you’re set.

To see everything in action:

1. Start the Opencode server:

2. Install the hook in any repo you want to test:

3. Make a deliberately bad change (introduce a typo, leave a hardcoded secret, add a debug statement) and try to commit:

If the AI reviewer catches something, you’ll see output like this:

The commit is blocked. Fix the issues, re-stage, and try again.

I used the Opus 4.6 model for testing and it consistently respected the JSON format and produced useful reviews. Other coding-focused models should work well too.

That said, a few caveats worth noting. Like any LLM output, the results aren’t deterministic — you may get slightly different feedback each time. You might also hit API rate limits depending on your usage. And while the model is surprisingly good at catching real issues, it can occasionally flag things that don’t matter. Treat it as a helpful second pair of eyes, not an infallible gatekeeper.