Three things lined up over the past few months and pushed me into building this. Token costs kept creeping upwards the more I leaned on cloud coding agents. Open weight models got genuinely good — good enough that “just run it locally” stopped sounding like a compromise. And underneath both of those, I just wanted to know for myself what’s actually possible when you cut the cloud out entirely, rather than take someone else’s word for it.
None of those three on their own would’ve been enough to get me off the couch. Together, they were.
This is part one of a series on localagent-box — something I built to answer that question. It’s a self-hosted orchestration layer that hooks OpenCode up to Ollama and GitHub. You give it a repo and a prompt, and it goes off and clones the code, creates a branch, runs an AI coding session with a local model, then commits and pushes the result back to GitHub. All on your own hardware. No cloud APIs, no usage bills.
The three things that pushed me here
The token bills kept climbing. Not one shocking invoice — a slow drift. The more I folded cloud-hosted agents into my day-to-day, the more sessions I was running, and the more each session cost as I asked them to do bigger chunks of work. Nothing that broke the budget, but enough that I started mentally flinching before kicking off a bigger job. That’s not a great place to be if you want to use these tools freely.
Open weight models stopped being the fallback option. A year ago, running a local model for coding felt like a novelty — something you’d do to prove a point, not to get real work done. Models like qwen2.5-coder changed that calculus. They’re not frontier-model good, but they’re good enough for a real slice of the work I throw at an agent, and that slice is only growing.
And I just wanted to see for myself. I could read benchmarks and other people’s takes all day, but the only way to actually know what a 7B model running on my own GPU can and can’t handle unsupervised is to build the harness and let it loose on real repos. This whole project is as much an experiment as it is a tool.
Why not just use OpenCode directly?
OpenCode is great — it’s a terminal-first AI coding tool. But it’s built for interactive sessions where you’re sitting there at the keyboard. I wanted something I could fire-and-forget: kick off a job via the web UI or an API call, go do something else, and come back to a branch ready to review.
LocalAgent-box is the orchestration layer in the middle — it talks to OpenCode, handles the git plumbing, and keeps concurrent jobs from stepping on each other.
How it works
The core flow is straightforward:
- Job comes in — via the API or the web UI
- Clone the repo — fresh workspace, checks out the base branch
- Create a feature branch — so the agent has a clean place to work
- Run OpenCode — pointed at the workspace, Ollama as the model provider, using whatever prompt you gave it
- Commit and push — stages everything, commits with your message, pushes back to GitHub
That’s the default “batch” mode — single prompt, single run, done. No back-and-forth, no follow-ups. Just a PR branch waiting for you when it’s finished.
What’s under the hood
The server is a Node.js/TypeScript HTTP API that manages the job queue, spawns worker processes for each agent run, and exposes a REST API for creating and monitoring jobs.
The UI is a React app (Vite + Tailwind) that shows you what’s running, lets you kick off new jobs, and streams the worker logs so you can see what the agent is actually doing.
The main bits:
- OpenCode CLI —
opencode rundoes the actual coding work - Ollama — local model server; OpenCode talks to this instead of a cloud API
- GitHub App — generates short-lived installation tokens per repo so workers can read and write to private repos without PATs kicking around everywhere
- Open Code Review - Open source code review harness
That last one is worth a mention. I’ve done this kind of GitHub App integration before so it was the natural choice — the server authenticates as a GitHub App and mints a fresh scoped token for each job rather than sitting on a hardcoded PAT. A bit more setup the first time, but it works cleanly.
What a job actually looks like
Kick off a batch agent via the API:
curl -X POST http://localhost:8081/api/v1/agents \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"repoId": "myorg-my-app",
"prompt": "Add input validation to the login form",
"baseBranch": "main",
"agentBranch": "agent/login-validation",
"commitMessage": "Agent: add input validation to login form",
"push": true
}'
You get an agent ID back immediately. Then it’s just polling GET /api/v1/agents/:id for status or tailing logs if you want to watch it run. Status goes queued → running → completed (or failed if something blows up).
So, did it work?
Once the hardware’s paid for, each agent run on qwen2.5-coder:7b costs basically nothing. No token counting, no surprise bills, no throttling at peak hours. That’s the cost side of the equation sorted — but the more interesting answer is how much real work it can actually do.
Give it something specific and scoped — “add input validation to this form handler” — and it nails it, most of the time. Ask it to “improve the error handling in this service” with no further context and you get something that technically compiles but isn’t what you wanted. It’s a floor lower than GPT-5 or Claude, and you feel that floor the moment your prompt gets vague.
There’s a bonus I wasn’t chasing but noticed anyway: privacy. Your code never leaves your network. If you’re working on anything sensitive, or just don’t love the idea of your whole codebase passing through a third-party API, running everything locally sidesteps that entirely.
So the honest answer, a few weeks in: local models won’t replace a frontier model for the hard stuff, but they’ve earned a real spot in the rotation for well-scoped, repetitive work. Which, if I’m being honest about how I spend most of my agent budget, is most of it.
What’s next: interactive sessions
Batch mode is great for well-scoped tasks where you know exactly what you want. But sometimes you want to poke at the agent while it’s running — redirect it, ask it to extend something, course-correct before it goes too far down the wrong path.
Part 2 is about adding interactive mode: instead of opencode run and exit, the worker starts up an opencode serve instance and keeps it running. You get a proper chat UI with a live transcript, can send follow-up messages, and hit Finish when you’re happy — which is when the commit and push actually happens.
The tricky part is that OpenCode’s server API wasn’t built with an orchestration wrapper in mind. There’s a bunch of work mapping its SSE event stream into something the UI can render properly, plus some XDG environment variable gymnastics to make sure concurrent agents don’t step on each other’s sessions.
More implementation detail in the next post — worker architecture, the GitHub App setup, and diving into interactive sessions. The code is (or will shortly be) open source at github.com/dkarzon/localagent-box; I’m splitting this into a series to walk through the build, not because it’s still half-finished.