One of the interesting things about developing software with AI is that sometimes the development process itself becomes the experiment.

That happened this week while working on EZ-Blog, the Java application I use to create and publish this website.

The immediate cause was mundane: I ran out of Codex tokens.

Codex has become an important part of my development workflow, particularly as EZ-Blog has evolved from a relatively simple JavaFX desktop application into a modular system with a shared publishing engine, persistence layer, and planned server-side capabilities.

But when the tokens ran out, the alternative was to spend another $100 for more.

I wasn't going to do that.

Instead, I decided this was a good opportunity to answer a question I had been thinking about anyway:

Can I build a useful AI coding-agent environment that runs entirely on my own computer using local open-weight models?

The answer, after considerably more experimentation than I expected, appears to be yes.  Here is a screenshot of that end result:

But getting there was instructive.

What I Actually Wanted

My requirements weren't particularly exotic.

I wasn't looking for an AI autocomplete engine. I wanted something closer to the way I use Codex: an agent capable of understanding an entire Git repository, examining multiple modules and files, developing an implementation plan, modifying code, running tools and tests, and reporting what it did.

Just as importantly, I wanted to remain in control.

My preferred AI-assisted development process is deliberately iterative:

AI analyzes → human reviews → AI implements → tools verify → human reviews again.

For architectural work, I also frequently use ChatGPT as a second reasoning layer. One AI may inspect the repository and propose an implementation while another critiques the architecture and the human remains responsible for deciding what actually gets built.

That process has worked remarkably well on EZ-Blog.

The challenge was reproducing enough of the Codex side of that equation locally.

First Attempt: OpenHands

My first serious experiment was OpenHands.

Conceptually, it looked promising: an autonomous software-development agent capable of working against a repository while using a locally hosted model.

I already had Ollama available, so I tried pairing OpenHands with a local coding model.

This quickly ran into an architectural problem.

OpenHands was running its agent environment in Docker. Instead of naturally operating against my existing Windows EZ-Blog repository, we ended up dealing with container workspaces and filesystem mounts.

At one point the agent believed its working directory was:

/workspace/project

That wasn't my actual development environment.

It could see a collection of files, but we were now spending our time debugging Docker mounts and trying to ensure that the AI's idea of the repository matched the repository I was actually editing.

Worse, performance was poor.

This violated one of the principles I try to follow in system architecture: don't introduce an abstraction layer unless it is buying you something worth its complexity.

Docker isolation can be extremely valuable. For this particular use case, however, it was solving a problem I didn't have while creating several new ones that I did.

OpenHands was abandoned.

The Model Question

There was another problem intertwined with the tooling question: model size.

I initially experimented with Devstral Small 2 24B. On my hardware it worked, but it was simply too slow for the kind of interactive development loop I wanted.

So I installed Qwen2.5-Coder 7B.

That changed the performance equation dramatically. The model responded quickly and could run primarily on the GPU.

But there was still an unanswered question:

Was a 7-billion-parameter model actually capable enough to function as a useful coding agent?

As it turned out, answering that question required first finding an agent environment that actually worked correctly.

OpenCode — and an Unwelcome Security Detour

Next came OpenCode.

This required installing it through npm.

Shortly afterward, Norton Behavioral Protection terminated PowerShell and Edge and reported a high-risk behavioral detection involving a temporary file.

That did not establish that OpenCode was malicious. Behavioral antivirus systems can generate false positives, particularly when developer tools launch shells and execute commands dynamically.

Nevertheless, the timing demanded investigation.

There have been real software-supply-chain attacks involving npm packages, and ignoring an unexpected endpoint-security warning immediately after installing a new npm-based development agent would have been irresponsible.

I stopped OpenCode, uninstalled it, examined npm's installation logs, and verified that its package had executed a postinstall script. Norton subsequently completed a full system scan without finding additional malware.

The warning may well have been a false positive.

But the episode changed the cost-benefit calculation. I didn't need another layer of uncertainty in what was supposed to be a simpler local development environment.

OpenCode was out.

Cline Looked Better — Until We Tested It

Next I tried Cline, running directly inside Visual Studio Code and connected to Ollama.

This looked much closer to what I wanted.

VS Code had the correct EZ-Blog repository open. Cline connected successfully to Qwen2.5-Coder. Responses were fast.

Then we tested whether it actually understood the repository.

The results were bizarre.

Cline reported test files that did not exist. On another attempt it invented a path to a PersistenceManager.java file and then asked me to provide the correct path to the file it had just invented.

Initially, this looked like a model-quality problem.

Perhaps Qwen 7B simply wasn't capable enough.

So we designed a more controlled experiment.

Instead of asking Qwen to reason about the project, I explicitly instructed it to invoke Cline's list_files tool against a known test directory.

This time we could see that the model did invoke the correct tool against the correct directory.

Cline reported:

No files were found in the specified directory.

Except the directory wasn't empty.

Running ls against the exact same directory from VS Code's integrated terminal immediately displayed the Java test files.

That was an important result.

The model wasn't the primary problem. The agent's filesystem integration was.

There were other warning signs as well. Cline complained that Git needed to be installed even while VS Code itself correctly displayed the repository's main branch and Git worked perfectly from the integrated terminal.

At that point I stopped.

I wasn't going to build a development workflow around an agent whose view of the filesystem disagreed with the IDE hosting it.

JetBrains: Almost

Since EZ-Blog is a Java/Maven project and I normally use IntelliJ IDEA, JetBrains seemed like the obvious next candidate.

JetBrains AI Assistant supports Ollama, and connecting it to my local Ollama server worked.

Unfortunately, actually activating the AI Assistant interface led into JetBrains account and payment requirements, including a request for credit-card information.

That defeated the purpose of this exercise.

I already had a local model. I already had a local IDE. I wasn't interested in providing payment information merely to place an interface between the two.

Another dead end.

The Simplest Architecture Won

Eventually, after trying several specialized AI coding agents, we arrived at the solution that in retrospect seems obvious:

Visual Studio Code itself.

Recent versions of VS Code have native AI agent capabilities, and Ollama now provides an official VS Code extension that exposes local models directly to that environment.

My installed VS Code version was already sufficiently current.

I installed the official Ollama extension and selected:

Qwen2.5-Coder 7B

The architecture suddenly became refreshingly simple:

VS Code → native Agent → Ollama → Qwen2.5-Coder 7B

No Docker.

No repository mount.

No npm-installed coding agent.

No cloud model.

No additional subscription.

And, most importantly, VS Code's agent was operating directly against the repository already open in the IDE.

Then We Tested It

After the previous failures, I wasn't willing to trust a polished-looking interface.

We tested it.

First I asked the local model to describe EZ-Blog.

Instead of immediately generating an answer, the VS Code interface visibly showed the agent reading project files. It correctly identified the major architectural components and described their responsibilities.

Then we gave it the same sort of concrete filesystem test that had exposed Cline's failure.

I asked it to enumerate every Java test file in a specific persistence package.

It found 13 files, reported their exact names, and gave the correct count.

That was a significant milestone because it demonstrated that the model was grounding its answer in the actual repository rather than inventing plausible Java filenames.

We then tested Maven project comprehension. It read the root pom.xml, correctly identified the declared reactor modules in order, distinguished them from ordinary directories, and even correctly observed that ezblog-server exists in the repository but is not currently declared as a module in the root POM.

That is exactly the kind of distinction a useful development agent needs to make.

The Final Test: Let It Change Something

Reading is one thing. An agent needs to write.

VS Code provides three useful interaction modes:

Ask, for questions about the project.

Plan, for analyzing a task and developing an implementation approach.

Agent, for actually performing the work.

That separation fits extremely well with the development methodology I already use.

For the final test, I switched to Agent mode and gave it an intentionally trivial but complete assignment.

It had to create a temporary file in the repository root containing exactly:

LOCAL_AGENT_WRITE_OK

Then it had to read the file back and verify its contents, delete it, and independently verify that the file no longer existed.

It followed the instructions exactly.

More importantly, it didn't merely say that it had deleted the file. It executed an explicit filesystem existence check and reported the result.

Finally, I stepped outside the agent entirely and ran:

git status --short

The result was empty.

The Git working tree was completely clean.

Test passed.

What We Ended Up With

The resulting development architecture is much simpler than several of the systems we tried along the way:

ChatGPT → architecture and independent reasoning

VS Code Plan + local Qwen → repository analysis and implementation planning

Human → review and authorization

VS Code Agent + local Qwen → bounded implementation

Maven + Git → independent verification

Codex → stronger implementation agent when the task warrants it

This does not mean Qwen2.5-Coder 7B has suddenly become equivalent to Codex.

It almost certainly hasn't.

What we demonstrated instead is something more architecturally interesting: model capability and agent infrastructure are separate variables.

A smaller model attached to reliable tools may outperform a much more capable model attached to a broken view of the filesystem.

Conversely, if Qwen eventually proves inadequate for a difficult architectural or implementation task, the new environment lets us replace the model without replacing the rest of the development architecture.

That is exactly the kind of separation of concerns I want.

And Now We Dogfood It

There is an appropriate bit of recursion to all of this.

I am writing this article about using a local AI coding agent to develop EZ-Blog.

I will now use EZ-Blog itself to read, edit, and publish this article to daconta.us.

And the next stage of EZ-Blog development will provide the real test.

The project is currently moving into its SQLite persistence work. Instead of immediately buying another block of Codex tokens, I am going to see how much of that work can be accomplished using this new local development stack.

We'll use the same discipline we've been using throughout the project: small steps, explicit plans, human review, automated tests, Git verification, and stronger AI models when the problem actually requires them.

I'll report on how it goes.

Because the interesting question is no longer whether a local coding model can answer programming questions.

It's whether a small, locally running model—given reliable tools, a well-structured codebase, good tests, and disciplined human supervision—can become a productive member of a real software-development process.