{"slug": "autocode-one-file-one-tool-and-permission-to-rewrite-itself", "title": "autocode — one file, one tool, and permission to rewrite itself", "summary": "Empero-org released autocode, a coding agent contained in a single Python file of about 640 lines that ships with exactly one tool, bash, and is permitted to rewrite its own source code while working. The runner copies runner.py into each project, checks between steps whether the file changed, keeps the previous version in .autocode/runner.prev.py, and re-execs itself if the new version compiles, while any file in .autocode/tools/ defining a SCHEMA and run() function is picked up on the next step. The agent compacts context when it passes 80% of the window, and the release notes a pre-release bug in which some OpenAI-compatible servers ignored tool_choice: \"none\" and let a one-line preamble become the entire new context.", "body_md": "# autocode — one file, one tool, and permission to rewrite itself\n\nautocode 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.\n\nAgent frameworks have a habit of growing. Every release adds a tool, every tool\n\nadds a schema, every schema adds a paragraph to the system prompt, and before long\n\nthe model spends its first few thousand tokens reading instructions about\n\ninstructions. Abacus sits deliberately at the full-featured end of that: plans,\n\napprovals, 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?**\n\nThe answer we shipped: one Python file, one tool, the standard library, and\n\npermission to rewrite itself.\n\nautocode on GitHub: [empero-org/autocode](https://github.com/empero-org/autocode)\n\n## One file, and it's yours\n\nInstall once, then run `autocode` in a project. The first run copies `runner.py`\n\ninto that directory and starts it. That file is the whole agent — the loop, the\n\nstreaming client, sessions, compaction, the REPL — in about 640 lines, with no\n\nimports outside the standard library.\n\nIt is copied, not imported, on purpose. The agent is told where its own source\n\nlives 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\n\nprevious version in `.autocode/runner.prev.py`, re-execs itself and carries on\n\nwith the same task. If it doesn't compile, the model is told about the syntax\n\nerror and the working version keeps running.\n\nSo 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.\n\n## One tool\n\nThe model gets `bash`. Nothing else. Commands run exactly as written — no\n\nwrappers, no rewriting, no parser second-guessing the model. The runner only does\n\nwhat a shell can't do for itself: a timeout that kills the whole process group, a\n\nclosed stdin so nothing waits for a human, a working directory that persists\n\nbetween calls, and long output cut to its head and tail with the full text saved\n\nto a file the agent can page through.\n\nSearch, edit, test, git — everything a model reaches for is already a command\n\naway.\n\n## Tools it writes itself\n\nWhen 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\n\nthe next step. A tool that fails to import doesn't disappear: it shows up to the\n\nmodel marked **BROKEN**, with the error attached, so the agent can fix what it\n\nwrote. The tools are plain Python in your repository — worth committing, if they\n\nturn out to be good.\n\n## A system prompt you can read in ten seconds\n\nThree lines. Who it is and where; work until the task is done and verify as you\n\ngo; 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\n\nknows about your project, it found out with the shell.\n\n## Long sessions, honestly\n\nA small agent still runs long sessions, so compaction had to be right. When the\n\ncontext passes 80% of the window, the model summarizes the conversation and the\n\nsummary replaces it — except your newest message and the latest step, the last\n\nmodel message with its tool results, which stay word for word. The summary\n\nrequest reuses the exact prefix of a normal request, so providers with prompt\n\ncaching reuse the cache.\n\nWe found a bug in it before release, and it's worth telling. The first version\n\nasked for the summary with `tool_choice: \"none\"`. Some OpenAI-compatible servers\n\nignore that field, so a model can answer \"summarize this\" with a tool call and a\n\none-line preamble — and that preamble became the entire new context. Against a\n\nscripted fake server, a session's request and its failing test output were\n\nreplaced by *\"Let me look at the test file first.\"*\n\nNow a reply that isn't a summary is rejected and retried as a plain transcript\n\nwith no tools to call, a failed compaction never blocks the session, and the full\n\ntranscript is archived before anything is replaced. There's a test for exactly\n\nthat case. Not a feature. A fix we'd rather tell you about than have you find.\n\n## Any endpoint\n\nThe first run opens a short setup. It marks the backends it can already use — a\n\nkey in your environment, a local server that's running — lists the server's\n\nmodels, runs a one-line test call and saves the config. OpenAI, OpenRouter,\n\nDeepSeek, Groq, a Qwythos on Ollama or llama.cpp: if it speaks`/chat/completions`, autocode runs on it.\n\nThinking is read from whichever field your server uses - `reasoning_content`,` reasoning`, OpenRouter's `reasoning_details`, or `<think>` tags left in the\n\nanswer and shown in the terminal next to the reply, with markdown, highlighted\n\ncode and LaTeX drawn as Unicode. The renderer is standard library too.\n\n## What it doesn't do\n\nautocode gives a model a shell and no approval step. That is the point of it, and\n\nit is also the reason to run it somewhere you can afford to be surprised: a\n\ncontainer, a VM, a repository you can reset. It needs bash, so Linux and macOS.\n\nAnd 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\n\nbefore it lands, that's Abacus.\n\nWe built autocode to answer a narrow question: how little an agent can start with\n\nand still do real work. It turned out to be one file, one tool, and a model that\n\nis allowed to make the rest.\n\n```\npip install empero-autocode\ncd your-project\nautocode\n```\n\nGet it on GitHub: [empero-org/autocode](https://github.com/empero-org/autocode) ·\n\nPyPI: [empero-autocode](https://pypi.org/project/empero-autocode/)\n\n*autocode is open source, Apache-2.0. Point it at a Qwythos on your own machine\n\nand see what it turns itself into.*", "url": "https://wpnews.pro/news/autocode-one-file-one-tool-and-permission-to-rewrite-itself", "canonical_source": "https://empero.org/writing/autocode", "published_at": "2026-09-11 12:26:52+00:00", "updated_at": "2026-09-11 12:39:49.802440+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "ai-products"], "entities": ["autocode", "Empero", "runner.py", "Abacus", "GitHub", "OpenAI"], "alternates": {"html": "https://wpnews.pro/news/autocode-one-file-one-tool-and-permission-to-rewrite-itself", "markdown": "https://wpnews.pro/news/autocode-one-file-one-tool-and-permission-to-rewrite-itself.md", "text": "https://wpnews.pro/news/autocode-one-file-one-tool-and-permission-to-rewrite-itself.txt", "jsonld": "https://wpnews.pro/news/autocode-one-file-one-tool-and-permission-to-rewrite-itself.jsonld"}}