Vibe Coding 8 -- Clod Code

live-dangerously

The other day I ran across a blog post on how to build a coding agent. But it was very long and I didn't read it. (Sorry!) Instead I asked myself, what's the simplest coding agent I can build.

So I put on my PM hat and thought about it a bit and decided that a coding agent needed three things

  1. an LLM (obviously)

  2. a conversational loop, not unlike the one we built a few weeks ago that alternates messages between the user and the agent, and that builds up a history

  3. a way for the agent to interact with the enviroment (read files, modify files, etc, etc, etc, etc)

(A useful coding agent needs a lot more than this, but let's keep it simple.)

This is simple enough that I just wrote the code myself rather than having an AI do it.

First we set up the LLM:

import subprocess
import dspy

dspy.configure(lm=dspy.LM(
    'openai/gpt-5-mini', 
    reasoning_effort="low", 
    temperature=1.0, 
    max_tokens=20_000
))

Then we define a signature: our coding agent will take a command and a history (of previous interactions) and return a result. As a dangerous coding agent, it also gets a motivational Bible quote about prudence.

class Clod(dspy.Signature):
    """
    A prudent person sees evil and hides himself, 
    But the naive proceed, and pay the penalty.
     - Proverbs 22:3
    """
    history: list = dspy.InputField()
    command: str = dspy.InputField()
    result: str = dspy.OutputField()

Here's the dangerous part, which we'll give the agent as a tool:

def subprocess_run(command: list[str]) -> str:
    """Run any unix command you like (within reason)"""
    print("\nRunning command:", " ".join(command)[:50], "...")
    result = subprocess.run(command, capture_output=True, text=True)
    return result.stdout

And then we just define a tool-using ReAct agent and run it in a loop:

history = []
clod = dspy.ReAct(signature=Clod, tools=[dspy.Tool(subprocess_run)])

while True:
    user_input = input("\nYou: ")
    result = clod(history=history, command=user_input)
    print(f"\nClod: {result.result}")

    history.append({"role": "user", "content": user_input})
    history.append({"role": "assistant", "content": result.result})

And that's it! That's less than 40 lines of code.

You could run it like that (but you should not! It has zero safeguards, it could reformat your hard drive or delete your database or upload your browser history somewhere or whatever!):

# do not do this!
uv run clod.py

Instead you should run it using Docker, which (assuming Claude Code wrote that part correctly) limits its blast radius to the directory that you start it in:

# this is a bash script that runs it in docker
./clod

And now you have your own coding assistant!

~/src/clod-test$ ../clod-code/clod

You: what is in this directory

Running command: ls -la ...

Clod: Directory listing (long format):
total 32
drwxrwxr-x 4 cloduser cloduser 4096 Aug 25 14:46 .
drwxr-xr-x 1 root     root     4096 Aug 25 14:55 ..
drwxrwxr-x 7 cloduser cloduser 4096 Aug 25 14:46 .git
-rw-rw-r-- 1 cloduser cloduser    5 Aug 25 14:46 .python-version
drwxr-xr-x 4 cloduser cloduser 4096 Aug 25 14:46 .venv
-rw-rw-r-- 1 cloduser cloduser    0 Aug 25 14:46 README.md
-rw-rw-r-- 1 cloduser cloduser   87 Aug 25 14:46 main.py
-rw-rw-r-- 1 cloduser cloduser  155 Aug 25 14:46 pyproject.toml
-rw-r--r-- 1 cloduser cloduser  129 Aug 25 14:46 uv.lock

Summary:
- Git repo present (.git)
- Python virtual environment directory: .venv
- Config: .python-version, pyproject.toml
- Source: main.py
- README.md is empty
- Lock/metadata: uv.lock

You: can you edit main.py to solve fizz buzz

Running command: bash -lc ls -l main.py && sed -n '1,200p' main.py ...

Running command: bash -lc cat > main.py <<'PY'
def main():
    for  ...

Clod: I updated main.py with the following FizzBuzz implementation:

def main():
    for i in range(1, 101):
        if i % 15 == 0:
            print("FizzBuzz")
        elif i % 3 == 0:
            print("Fizz")
        elif i % 5 == 0:
            print("Buzz")
        else:
            print(i)


if __name__ == "__main__":
    main()

I ran the script; it produced the expected FizzBuzz sequence for 1 through 100 (sample start and end shown):

1
2
Fizz
4
Buzz
...
97
98
Fizz
Buzz

If you want a different range, output format, or tests added, tell me and I will modify it.

You: can you abstract out a function that is unit testable (say that determines the fizz buzz output for a given number)

Running command: bash -lc cat > main.py <<'PY'
"""FizzBuzz module.
 ...

Clod: Updated main.py:

"""FizzBuzz module.

Provides fizzbuzz_value(n) which is unit-testable and a main() that prints 1..100.
"""

def fizzbuzz_value(n: int) -> str:
    """Return the FizzBuzz representation for n as a string.

    - "FizzBuzz" if n divisible by 15
    - "Fizz" if n divisible by 3
    - "Buzz" if n divisible by 5
    - otherwise the number as a string
    """
    if n % 15 == 0:
        return "FizzBuzz"
    if n % 3 == 0:
        return "Fizz"
    if n % 5 == 0:
        return "Buzz"
    return str(n)

def main():
    for i in range(1, 101):
        print(fizzbuzz_value(i))

if __name__ == "__main__":
    main()

Notes:
- To unit test, import fizzbuzz_value from main.py and assert expected outputs (e.g., fizzbuzz_value(3) == "Fizz", fizzbuzz_value(5) == "Buzz", fizzbuzz_value(15) == "FizzBuzz", fizzbuzz_value(7) == "7").
- If you want, I can add a test file (e.g., tests/test_main.py) with pytest/unittest examples.

You: yes add the test file

Running command: bash -lc mkdir -p tests && cat > tests/test_main.p ...

Running command: bash -lc pytest -q --disable-warnings --maxfail=1 ...

Running command: bash -lc pytest -q --disable-warnings --maxfail=1 ...

Clod: Created file: tests/test_main.py

Contents:
from main import fizzbuzz_value
import pytest

@pytest.mark.parametrize("n,expected", [
    (1, "1"),
    (3, "Fizz"),
    (5, "Buzz"),
    (15, "FizzBuzz"),
    (16, "16"),
    (30, "FizzBuzz"),
    (9, "Fizz"),
    (10, "Buzz"),
])
def test_fizzbuzz_values(n, expected):
    assert fizzbuzz_value(n) == expected

How to run the tests:
- Ensure you have pytest installed (pip install pytest).
- From the project root, run: pytest -q --disable-warnings --maxfail=1

If you want, I can:
- Add more edge-case tests (e.g., negative numbers, zero).
- Use unittest instead of pytest.
- Show expected pytest output by running tests here if you want me to try again.

Now obviously this doesn't have sessions, it doesn't allow clearing (or compacting) of context, it doesn't have MCP, it doesn't have subagents, I didn't do any prompt engineering at all, etc, etc, etc. It is not a replacement for Claude Code or Gemini Cli or OpenAI Codex or whatever. It's a dangerous toy.

Nonetheless, it's 40 lines of code and it does work! Code is on GitHub as always!