coding-agent
A coding agent built from scratch in Python, on Gemini.
- built
- April 2026
- stack
- Python, Gemini (google-genai), Textual
- size
- about 1,600 lines, 6 tools, CLI and terminal UI
- code
- github.com/ismajl-ramadani/coding-agent
Why
I use coding agents every day, and I wanted to know what actually happens inside one. Not the prompt, the machinery: how tool calls go back and forth, how you stream the model's reasoning separately from its answer, how you stop it from wandering outside the project, and how you let a human approve a plan before anything gets written. The fastest way to find out was to build one without a framework.
The loop
The model either answers in text, which ends the turn, or asks for tools. Tool results go back into the conversation and the model goes again. Getting the conversation history exactly right (the model's tool calls on its side, the results on the user's side) is most of the work; get it wrong and the model loses track of what it did.
Tools
- list files and read file, with reads cut at 10,000 characters and the model told when that happened.
- write file, creating folders as needed.
- patch file, a search-and-replace edit that fails if the search text isn't unique. That one rule forces the model to quote enough context and stops it editing the wrong place.
- run command with a 120-second timeout, and git with arguments passed as a list, never as a shell string.
Keeping it on a leash
- Every path is resolved and checked against the project folder before any read or write. Anything outside is refused.
- The working folder is set by the program, not the model. If the model claims a different one, it's overwritten.
- A hard cap of 50 turns, so a confused model stops with a clear error and doesn't spin forever.
- Tool responses are validated, so a protocol mistake shows up as an error immediately, not as a quietly broken conversation.
Plan first, then execute
/plan <task> runs the agent with read-only tools and a prompt that asks for a structured plan: goal, steps, files to change, risks. Writes are blocked in this mode, and the model is told so, so it doesn't crash. The plan is saved to disk; /plans lists them and /run executes one and marks it done. It's the "approve before acting" flow I rely on in other tools, but the plan survives across sessions.
Sessions and the terminal UI
- Every turn is saved as it happens, so an interrupted run loses nothing.
--resumepicks up the last session. - The terminal UI (Textual) streams the answer as markdown, shows the model's thinking in a panel that folds away once the answer starts, and shows each tool call as a card you can expand.
- The agent runs on a worker thread and pushes chunks to the UI in order, so a burst from the model never renders out of sequence.
What I took away
The model is the easy part. The hard parts are the boring ones: exact edit semantics, sandboxing, a history format the model can follow, and good feedback when something fails. Those are also what I now look at first when I evaluate any AI tool.