
Brain in a jar
I finally got around to writing a simple coding agent from scratch, and seeing it work was equal parts unnerving and amazing.
The closest comparison I can think of is when I was 8 and figured out how to use a lighter. I could make stuff happen with it, and it was fascinating to see how different materials reacted to its heat. As you can imagine, this little fixation led to some not-so-fun consequences.
The agent I made isn’t exactly fire, but its power and unpredictability make it feel like fire whenever it runs. And this is the simplest coding agent I could piece together:
gitlab.com/rgreen32/basiccodingagent
The whole thing fits on one screen

That’s basically all of it. It runs on Bun and talks to a model through OpenRouter. There are two other small files: one sends the conversation to the model, and the other describes a single tool called bash that takes one argument, command.
The loop works like this:
- A system prompt tells the model it’s an autonomous coding agent and should do things with its tool instead of explaining how to do them.
- I type a task, and it’s added to the conversation as a user message.
- The whole conversation is sent to the model, along with the description of the
bashtool. - If the model replies with a tool call, my code pulls the command out, runs it with
sh -c, and adds the output to the conversation as atoolmessage. Then it goes back to step 3. - If the model replies with plain text, the agent prints it and waits for my next task.

This pattern is usually called a ReAct loop (reason + act). The model thinks about what to do, takes an action, looks at the result, and repeats until it decides it’s done. It’s not a fixed script. The model chooses every step, so if a command fails, it reads the error and tries something else.
Why “brain in a jar”
One thing to remember: the model can’t actually do anything. It never touches my machine. All it can do is produce text.
What makes this work is tool use. Models are explicitly fine-tuned to emit specially formatted “tool call” output when it’s appropriate, instead of a normal reply. The API hands that back to me as a structured tool_calls object, something like {"name": "bash", "arguments": {"command": "ls"}}. Running it is entirely up to my code.
So the model is a brain in a jar. It can think and it can ask for things, but it has no hands. My 30 lines of TypeScript are the hands. They take whatever the brain asks for and run it, no questions asked.
That’s what’s both amazing and unnerving. The intelligence lives in the model, but the power lives in the harness. And mine gives it a full shell.
Playing with fire
I only had it do simple things for testing, like creating directories and files. But it’s clearly capable of much more, and that has scared me off from doing anything significant with it, because there’s no sandboxing and no guardrails. Whatever command the model comes up with runs with my permissions, on my machine.
If I keep building on it, here’s what I’d add first:
- A sandbox. Run the commands in a container or VM so a bad command can only wreck something disposable.
- Approval before running. Show me each command and wait for a yes before executing it, at least for anything that writes, deletes, or touches the network.
- Timeouts and output limits. A command that hangs or prints a huge amount of output shouldn’t stall the loop or flood the conversation.
- Handling every tool call. Right now it only runs the first tool call in each response. A model can ask for several at once.
- A step limit. Cap how many times the loop can run on its own before it has to check in with me.
None of that changes the core loop. It’s all about the hands, which is kind of the point: the scary part of an agent isn’t the brain, it’s what you let the brain reach.
Takeaways
Building this cleared up a lot of the mystery around coding agents for me. Strip away the polish and every one of them has the same skeleton: a loop, a model that can ask for tools, and a harness that decides what those requests are allowed to do. The difference between a toy and something you can trust is almost entirely in that last part.
If you’ve been curious how these things work, I recommend building one yourself. It doesn’t take long. Just maybe don’t hand it a lighter on day one.