Skip to main content

autocode starting up in a terminal

autocode

A minimal coding agent that lives in your project as one Python file and is allowed to rewrite itself.

pip install empero-autocode
cd my-project
autocode                 # the first time, a short setup picks the backend and model

Setup lists common providers and marks the ones it can already use (an API key in your environment, or a local server that's running). It then lists the server's models, runs a one-line test call, and saves the result to ~/.config/autocode/config.json. Keys found in the environment are saved as a $VAR reference, never the key itself.

The first run copies runner.py into the current directory and runs it. Every later run uses that local copy, which is never overwritten. The agent can edit its own copy, so each project's agent can evolve on its own.

The idea

  • One file, stdlib only. runner.py is the whole agent: an agent loop, streaming client, sessions, compaction and REPL, in about 860 lines. You can also run it directly with python runner.py.

  • One built-in tool: bash. Commands run as written. The working directory persists between calls, stdin is closed, and there's a timeout. Long output is clipped, with the full text saved to a file the agent can page through.

  • The agent writes its other tools. Any .autocode/tools/<name>.py that defines SCHEMA and run(**args) -> str is picked up on the next step:

    SCHEMA = {"name": "add", "description": "Add two numbers.",
              "parameters": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}}}
    
    def run(a, b):
        return str(a + b)
    

    A tool that fails to import still shows up, marked BROKEN with its error, so the agent can see what went wrong and fix it.

  • Self-modification. Before each model call the runner checks whether runner.py changed. If it still compiles, the runner saves the session, backs up the running version to .autocode/runner.prev.py, re-execs itself and continues the same turn. If it doesn't compile, the agent is told about the syntax error and the current version keeps running.

  • Compaction. It runs when the context passes compact_at × context_window (always leaving room for a max_tokens reply), or when the server rejects a request for being too long.

    • The model summarizes the conversation, and the summary replaces everything except your newest message and the latest step (the model's last message and its tool results), which are kept word for word.
    • The summary request starts with the same messages as a normal request, so the provider's prompt cache still applies.
    • If the server answers with a tool call instead of a summary, which happens on servers that ignore tool_choice, compaction retries with the conversation as a plain transcript and no tools.
    • If compaction fails, the agent says so and keeps working.
    • Full transcripts are kept in .autocode/sessions/old/.
  • A minimal system prompt. It is three lines, plus AGENTS.md (or CLAUDE.md) if the project has one. The first line tells the model that a person is at the keyboard, so when a request is ambiguous it asks instead of guessing.

  • Readable output. Replies render as they stream: markdown headings, lists, quotes and tables, syntax-highlighted code blocks, and LaTeX math as Unicode (\frac{1}{2}\alpha^2 becomes ½α²). Your messages appear as a gray bar and thinking is gray italic (including <think> tags some servers leave in the answer). Tool calls show as ● with an output preview, and a blank line separates each block. The renderer is the package's printing module, autocode/tui.py (stdlib only). runner.py imports it when it's available and falls back to plain text, and output that isn't going to a terminal is left untouched. Try it on any file with python -m autocode.tui README.md.

Safety

autocode runs commands as written and can rewrite its own runner.py — that is the product, not a bug. It has no sandbox, no permission prompts, and no dry-run mode: a bash call is a real shell on your machine with your user and your environment, and the agent's edits take effect live, before the next model call.

So run it where a mistake is acceptable. Use a container, a VM, or a throwaway copy of the project if the code or data in it matters. The same applies to any keys and credentials the shell can read: the agent can run bash and the tool it writes can too, so anything in your environment is in its reach. If that bothers you, keep OPENAI_API_KEY out of AUTOCODE_-style env expansion or run against a local server, and treat runner.prev.py as your rollback, not an undo button.

Usage

autocode                         # interactive
autocode "fix the failing test"  # start with a prompt
autocode -p "summarize this repo" > summary.md   # one-shot: answer on stdout, trace on stderr
git diff | autocode -p "review this"             # piped stdin is appended to the prompt
autocode -c                      # continue the latest session
autocode -r 20260911-014900      # resume a specific session
autocode -m some-other-model     # override the model
autocode --setup                 # pick the backend and model again
autocode --diff                  # how has this project's runner drifted from the original?
autocode --reset                 # restore the original runner.py (old one → .autocode/runner.prev.py)

In the REPL, /help lists the commands:

Command What it does
/model [name|filter] Lists the server's models, or switches to one. It asks whether to keep the choice for this session (the default), this project, or all projects.
/config [key [value]] Shows every setting and where it comes from, or saves one to .autocode/config.json. /config unset <key> removes it again.
/reset Updates runner.py to the installed version, backs yours up to .autocode/runner.prev.py, and reloads.
/compact Summarizes the conversation now.
/new Starts a new session.
/exit Quits (so does Ctrl-D).

End a line with \ to continue it on the next line. Ctrl-C interrupts the current turn.

To steer the agent while it works, just start typing. A prompt opens at the bottom and output pauses while you type. Enter queues the message, and Esc cancels it. A queued message reaches the model before its next step, right after the current reply and its tool calls. If the agent was about to finish, it carries on and answers your message. If the turn ends while you're still typing, your text moves into the normal prompt.

⚠️ autocode runs bash as written and rewrites its own source. Use it in a sandbox or on projects where a mistake is acceptable — the safety features are runner.prev.py and your backups, nothing else.

Configuration

Works with any OpenAI-compatible /chat/completions endpoint, such as OpenAI, OpenRouter, vLLM, llama.cpp, Ollama, LM Studio, Groq or DeepSeek.

Settings are applied in this order, later ones winning: built-in defaults, then ~/.config/autocode/config.json, then .autocode/config.json, then AUTOCODE_<KEY> environment variables, then -m. OPENAI_BASE_URL and OPENAI_API_KEY are used as fallbacks.

{
  "base_url": "https://openrouter.ai/api/v1",
  "api_key": "$OPENROUTER_API_KEY",
  "model": "qwen/qwen3-coder",
  "context_window": 262144,
  "compact_at": 0.8,
  "output_limit": 30000,
  "timeout": 600,
  "temperature": null,
  "max_tokens": null,
  "keep_reasoning": false,
  "headers": {},
  "extra_body": {"reasoning_effort": "high"}
}
  • api_key and headers values expand $VARS, so keys don't have to be stored in files.
  • extra_body is merged into every request. Set a key to null to remove it, for example "stream_options": null for servers that reject it.
  • Thinking is read from reasoning_content, reasoning, OpenRouter's reasoning_details (text and summaries), or <think> tags in the answer. reasoning_details, which includes signatures and encrypted blocks, is always sent back, because providers need it to continue a tool loop. keep_reasoning also sends the plain reasoning text back, which some thinking models require.
  • For a local Ollama server: AUTOCODE_BASE_URL=http://localhost:11434/v1 AUTOCODE_MODEL=qwen3-coder autocode.

Files

runner.py                 the agent (yours to change)
.autocode/config.json     project config (gitignored)
.autocode/tools/          tools the agent wrote (worth committing)
.autocode/sessions/       transcripts, one JSON message per line (gitignored)
.autocode/out/            full text of clipped tool output (gitignored)
.autocode/runner.prev.py  the runner version before the last self-edit

Development

python -m unittest discover tests   # end-to-end against a scripted fake server

License

Apache-2.0: see LICENSE. If you redistribute autocode or build on it, keep the attribution in NOTICE, as Section 4(d) of the license requires.

Metadata

Release files for empero-autocode 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for empero-autocode 0.1.1
File Size Uploaded Provenance
empero_autocode-0.1.1.tar.gz 51.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for empero-autocode 0.1.1
File Interpreter ABI Platform
empero_autocode-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 92.7 kB

Release files / empero_autocode-0.1.1.tar.gz

Download URL empero_autocode-0.1.1.tar.gz
Size 51.3 kB
Tags Source
SHA-256 checksum
How to use checksums
614db1f5aa276f79c54439b06ac4e50eb0e0e4a11ac446d7935548db49ff465b
BLAKE2b-256 checksum
How to use checksums
3700769b88b09a380807dceb58b7fd66ca930f2d4c3b721699245ff4d4cb2095
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / empero_autocode-0.1.1-py3-none-any.whl

Download URL empero_autocode-0.1.1-py3-none-any.whl
Size 41.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b43a6235fda689f68ad1785ba7498cc86272bd8811421a6d80a71e78fbe990c3
BLAKE2b-256 checksum
How to use checksums
63998d11bb4cdc4b481a18cfef1a481d6525dcea9b87557be490c312ec600d40
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for empero-autocode 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page