I Built My Own Coding Agent and You Should Too
Gopher in a pod

But Why?

Already so many choices

There are already a plethora of CLI coding agents available - Aider, OpenCode, Claude Code, and Codex - not to mention newcomer general-purpose agents like Openclaw. Why would anyone want to build yet another one?

Disparate feature sets

Yes, I use several coding agents, but each lacks features I want. Aider's 'watch' mode stands out, monitoring project changes and acting on 'AI!' comments, while others lack these features. Some support skills, others don't.

Too much interactivity

Most importantly, they all seem entirely interactive, which misses a key point of CLI coding agents. If you want to use these in a complex UNIX pipe or run them from a CRON job, you can't. Or what if you want to build your own AI-enabled bash script? Wouldn't it be nice to pop into your project directory, type a command, and have it analyze changes, commit them, and write a clear commit message for you?

To learn how they work

Reading open-source agent code is helpful, but for really understanding how coding agents function and how to make them suit my needs, building my own provides hands-on insight that no documentation could.

What tools do you need to make available to the agent to allow it to be effective at writing code? How do I incorporate the fancy new skills that have recently hit the scene?

The Most Important Reason

Because I can.

What I learned

Lesson 1: It Was Easier Than I thought

I used my standard Python setup for this project. I used uv to manage the virtual environment (.venv) and dependencies. I also included a Justfile for simple task running - like to run the module and linting.

For interacting with LLMs, there are numerous libraries that fit the bill, and the HTTP API is not too difficult to manage. I chose Pydantic-AI, developed by the makers of the Pydantic library. Pydantic-AI included many of the tools needed for interacting with the LLM, especially if they use the OpenAI-style API (most providers do). It has tool-calling capabilities built in. For skills, Doug Trejano made the venerable pydantic-ai-skills plugin, which makes short work of using skills.

Getting something up and running was reasonably simple - especially using a coding agent (coding agent to build a coding agent - who would have thought) to bulk some of it out quickly.

To get basic code editing done, I only needed to build 3 basic tools:

  1. read_file - returns the contents of a specified file to the LLM.
  2. edit_file - takes SEARCH/REPLACE patches and makes the needed changes.
    <<<<<<< SEARCH
    
    existing text (leave blank to insert without matching)
    
    =======
    
    replacement text
    
    >>>>>>> REPLACE
    
  3. bash - allows the LLM to run arbitrary bash commands - yes, this may be a bit dangerous.

Otherwise, setting up a basic REPL loop to read user input, send it to the LLM, and print the output was pretty straightforward. Of course, this original implementation was basic, and everything was executed synchronously. Pydantic-AI handled all the tool-calling (to start out, I didn't implement tool approval).

Next, I added in a YAML configuration file to configure the agent and included some shell arguments to allow run-time configuration of the agent - namely around model selection.

Additionally, I added a way to pipe in the output of other commands and a way to specify a prompt directly on the command line.

Watch mode was a bit more work, but mostly well understood. I used the watchdog library to monitor the file system for changes and trigger the agent when it sees a line ending with 'AI!'. I made sure to pay attention to the .gitignore file so it doesn't trigger on changes to ignored files.

Since I wanted to allow single execution calls from the CLI, I needed a way to persist the interaction across separate invocations. To give the LLM agent memory across multiple invocations, I chose to store the interaction history in a .sessions directory. I also allowed the user to specify a session name as an argument, so multiple sessions can be kept separate.

Lesson 2: It Was Harder Than I Thought

After playing around with this basic implementation, I wasn't quite happy. Yes, it worked, and did a fair job implementing code changes I requested, but sometimes it would churn for several minutes, and I had no idea what it was doing. I wanted the fancy streaming output so I could watch the LLM work. In addition, I needed a little bit of security around the tool-calling, especially the bash tool. There have been several horror stories of an LLM running rampant with access to the terminal.

At this point, the original project was getting a bit unwieldy - everything was packed into a single file with no visible architecture. Now that I had a basic understanding, I started with a clean project. In the early stages of a new project, this can be an excellent idea. Of course, saying I was starting from scratch wouldn't be entirely accurate, since I had a fair bit of code I could copy over, but it was a good opportunity to rethink the architecture and make it more modular.

Implementing streaming output took some work. Of course, using async Python made it easier, but diving into the Pydantic-AI documentation and code was necessary. There were a few gotchas to work through, but in the end, I got it working.

When it comes to approving tool calls, I struggled a bit to figure out how to implement it. The documentation had examples, but not using the specific asynchronous iterator style I was using. This took a lot of trial and error to get this working.

After getting tool-calling working, I added in some slash commands to allow the user to perform other tasks - like listing available models, seeing the history of the session, and clearing the session history.

In the end

Am I finished? No, like any good project, it is never finished, but I have a working agent that I can easily customize to my preferences. I know I can easily extend it to my needs, customize tools, and add new skills as needed. This also gives me a stepping stone to other ageentic AI projects that I may want to build in the future.

Source Code

If you are interested, you can see the source code. This may be A good reference for building your own agent