# autocode — one file, one tool, and permission to rewrite itself

> Source: <https://empero.org/writing/autocode>
> Published: 2026-09-11 12:26:52+00:00

# autocode — one file, one tool, and permission to rewrite itself

autocode is out — a coding agent that fits in a single Python file, ships with exactly one tool, and is allowed to edit its own source while it works. No dependencies, no framework: the least an agent needs, if it gets to build the rest.

Agent frameworks have a habit of growing. Every release adds a tool, every tool

adds a schema, every schema adds a paragraph to the system prompt, and before long

the model spends its first few thousand tokens reading instructions about

instructions. Abacus sits deliberately at the full-featured end of that: plans,

approvals, subagents. autocode is the experiment at the other end — **what is the least an agent needs, if it is allowed to build the rest itself?**

The answer we shipped: one Python file, one tool, the standard library, and

permission to rewrite itself.

autocode on GitHub: [empero-org/autocode](https://github.com/empero-org/autocode)

## One file, and it's yours

Install once, then run `autocode` in a project. The first run copies `runner.py`

into that directory and starts it. That file is the whole agent — the loop, the

streaming client, sessions, compaction, the REPL — in about 640 lines, with no

imports outside the standard library.

It is copied, not imported, on purpose. The agent is told where its own source

lives and that it may improve it. Between steps the runner checks whether`runner.py` changed; if the new version compiles, it saves the session, keeps the

previous version in `.autocode/runner.prev.py`, re-execs itself and carries on

with the same task. If it doesn't compile, the model is told about the syntax

error and the working version keeps running.

So every project ends up with its own agent, shaped by the work done there.`autocode --diff` shows how far it has drifted from the one we shipped.`autocode --reset` puts it back.

## One tool

The model gets `bash`. Nothing else. Commands run exactly as written — no

wrappers, no rewriting, no parser second-guessing the model. The runner only does

what a shell can't do for itself: a timeout that kills the whole process group, a

closed stdin so nothing waits for a human, a working directory that persists

between calls, and long output cut to its head and tail with the full text saved

to a file the agent can page through.

Search, edit, test, git — everything a model reaches for is already a command

away.

## Tools it writes itself

When a shell isn't enough, the agent writes a tool. Any file in`.autocode/tools/` that defines a `SCHEMA` and a `run()` function is picked up on

the next step. A tool that fails to import doesn't disappear: it shows up to the

model marked **BROKEN**, with the error attached, so the agent can fix what it

wrote. The tools are plain Python in your repository — worth committing, if they

turn out to be good.

## A system prompt you can read in ten seconds

Three lines. Who it is and where; work until the task is done and verify as you

go; your own source is editable; here is how to add a tool. Plus the project's`AGENTS.md`, if there is one. That's the whole prompt. Everything else the model

knows about your project, it found out with the shell.

## Long sessions, honestly

A small agent still runs long sessions, so compaction had to be right. When the

context passes 80% of the window, the model summarizes the conversation and the

summary replaces it — except your newest message and the latest step, the last

model message with its tool results, which stay word for word. The summary

request reuses the exact prefix of a normal request, so providers with prompt

caching reuse the cache.

We found a bug in it before release, and it's worth telling. The first version

asked for the summary with `tool_choice: "none"`. Some OpenAI-compatible servers

ignore that field, so a model can answer "summarize this" with a tool call and a

one-line preamble — and that preamble became the entire new context. Against a

scripted fake server, a session's request and its failing test output were

replaced by *"Let me look at the test file first."*

Now a reply that isn't a summary is rejected and retried as a plain transcript

with no tools to call, a failed compaction never blocks the session, and the full

transcript is archived before anything is replaced. There's a test for exactly

that case. Not a feature. A fix we'd rather tell you about than have you find.

## Any endpoint

The first run opens a short setup. It marks the backends it can already use — a

key in your environment, a local server that's running — lists the server's

models, runs a one-line test call and saves the config. OpenAI, OpenRouter,

DeepSeek, Groq, a Qwythos on Ollama or llama.cpp: if it speaks`/chat/completions`, autocode runs on it.

Thinking is read from whichever field your server uses - `reasoning_content`,` reasoning`, OpenRouter's `reasoning_details`, or `<think>` tags left in the

answer and shown in the terminal next to the reply, with markdown, highlighted

code and LaTeX drawn as Unicode. The renderer is standard library too.

## What it doesn't do

autocode gives a model a shell and no approval step. That is the point of it, and

it is also the reason to run it somewhere you can afford to be surprised: a

container, a VM, a repository you can reset. It needs bash, so Linux and macOS.

And an agent that rewrites itself can rewrite itself badly — that's what`runner.prev.py` and `--reset` are for. If you want every change shown as a diff

before it lands, that's Abacus.

We built autocode to answer a narrow question: how little an agent can start with

and still do real work. It turned out to be one file, one tool, and a model that

is allowed to make the rest.

```
pip install empero-autocode
cd your-project
autocode
```

Get it on GitHub: [empero-org/autocode](https://github.com/empero-org/autocode) ·

PyPI: [empero-autocode](https://pypi.org/project/empero-autocode/)

*autocode is open source, Apache-2.0. Point it at a Qwythos on your own machine

and see what it turns itself into.*
