python-agent-harness
A lightweight Python coding-agent harness for reliable autonomous coding.
FSM-driven execution · OpenAI-compatible · built for daily use and easy customization
A terminal coding agent that reads your codebase, plans changes, edits files, runs commands, and verifies its work.
python-agent-harness is inspired by gptel-agent-harness and opencode. It brings opencode's prompts and core behaviors—such as AGENTS.md discovery, plan/build modes, skills, sub-agents, and todo tracking—into a lightweight Python implementation with only three runtime dependencies:
richhttpxprompt_toolkit
It works with any OpenAI-compatible API and is designed to be easy to inspect, customize, and use for everyday software development.
Demo
Quick start
git clone git@github.com:beacoder/python-agent-harness.git
cd python-agent-harness
make install
. venv/bin/activate
python-agent-harness config --init
python-agent-harness run
Edit ~/.config/python-agent-harness/config.json and set your base_url, api_key, and model.
Optional extras:
pip install -e ".[mcp]" # MCP server integration
pip install -e ".[dev]" # development tools
Features
- FSM-driven execution — explicit
WAIT/TOOL/TRET/SUPERVISE/DONE/ERRS/ABRTstates. Completion supervision nudges the model when it stops early, while failed tool calls are sanitized so they never strand the agent. Transient API failures (429/5xx) retry with exponential backoff and jitter. - Context management — CJK-aware token estimation, per-model context windows, and automatic compaction at 70% usage.
- Coding tools —
Agent,TodoWrite,Glob,Grep,Read,Insert,Edit(including unified diffs),Write,Mkdir,Bash,Skill,Question, andPlanExit. Synchronous tools execute sequentially; asynchronous tools such asBashandAgentcan run concurrently while preserving emitted order. - Plan / Build modes — plan mode is read-only except for the per-session plan file.
- Persistent sessions — sessions are automatically saved after every response to
~/.local/share/python-agent-harness/sessions/, with LLM-generated titles and support for/restore --latestand/sessions. - Focused TUI — a Rich-based interface with a pinned status bar, Todos panel, inline red/green diff rendering for
EditandWrite, and aprompt_toolkiteditor with history and completion.Esc+Entersubmits,Ctrl-Dquits, andCtrl-Ccancels without leaving the application. - MCP support — optional MCP integration through the
[mcp]extra. MCP tools become ordinary agent tools such asmcp__<server>__<tool>. Supportsstdio,streamable-http, andssetransports. - Slash commands — built-in
/init,/review,/explain, and other commands, plus custom commands loaded fromprompts/commands/*.md.
Inspired by opencode
Most of opencode's prompts and core behaviors have been ported to this project. The goal is to retain its practical coding-agent workflow while keeping the implementation small, dependency-light, and easy to customize.
Prompt and behavior mapping
The following opencode prompts have corresponding implementations in python-agent-harness:
| opencode | python-agent-harness |
|---|---|
default.txt (main agent) |
agent.md |
plan.txt / plan-mode.txt / build-switch.txt |
plan.md / plan-mode.md / build-switch.md |
task.txt (sub-agent) |
subagent.md + Agent tool |
todowrite.txt / question.txt / skill.txt |
TodoWrite / Question / Skill tools |
read.txt / write.txt / edit.txt / grep.txt / glob.txt |
Read / Write / Edit / Grep / Glob tools |
shell.txt |
Bash tool + agent.md Git/GitHub guidance |
plan-enter.txt / plan-exit.txt |
PlanExit tool |
initialize.txt / review.txt / explain |
initialize.md / review.md / commands/explain.md |
| compaction / summary / title | compact.md / summary.md / title.md |
AGENTS.md handling |
prompts.py (find_agents_md_files, load_context_files, per-file resolution) |
Configuration
All LLM settings live in a single JSON configuration file. Environment variables are optional.
{
"llm": {
"base_url": "https://api.openai.com/v1",
"api_key": "sk-...",
"model": "gpt-5-mini",
"reasoning_effort": null,
"stream": true
},
"models": {
"_comment": "Named LLM profiles for /model switching. Partial settings; unset keys inherit the main llm.",
"deepseek": {
"base_url": "https://api.deepseek.com/v1",
"model": "deepseek-chat"
},
"qwen": {
"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"model": "qwen3.5-coder"
}
},
"subagent_llm": {
"profile": null,
"base_url": null,
"api_key": null,
"model": null,
"temperature": null,
"max_tokens": null,
"timeout": null,
"reasoning_effort": null,
"stream": null
},
"paths": {
"context_path": null,
"skill_path": null
},
"mcp": {
"servers": {
"example": {
"transport": "stdio",
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/tmp"
],
"env": [],
"parallel": false,
"timeout": null,
"enabled": false
}
}
}
}
Configuration options
llm— main LLM configuration. Optional keys includebackend,temperature,max_tokens,timeout,reasoning_effort, andstream. Values such asreasoning_effortare passed to the API as-is when set.run --no-streamoverridesstream.models— named LLM profiles for runtime switching with/model. A profile is a partial settings dictionary; unset keys inherit from the mainllm.defaultrestores the main LLM configuration.subagent_llm— LLM configuration forAgenttool requests. Unset values inherit from the mainllm. Setprofileto reuse a profile frommodels. Precedence is: profile settings > explicitsubagent_llmsettings > mainllm> environment variables.paths.context_path/paths.skill_path— locations from which to load context files and skills. When unset, the project-local<project>/contextsand<project>/skillsdirectories are used.mcp.servers— MCP server configuration. Requires the[mcp]extra. Each server supportstransport,command,args,env,url,headers,parallel,timeout, andenabled.- Configuration precedence — code defaults < config file <
OPENAI_*environment variables. Sub-agent settings also supportOPENAI_SUBAGENT_*(_BASE_URL,_API_KEY,_MODEL,_BACKEND). - Custom config — use
--config PATHorPYTHON_AGENT_HARNESS_CONFIG. - LLM logging — request and response bodies are logged as JSON to
/tmp/python-agent-harness-<date>-<id>.json. SetLLM_LOG_DIRto change the directory. The log path is printed at startup.
Usage
python-agent-harness run [project-dir]
Launches the interactive TUI agent. If project-dir is omitted, the current directory is used.
Slash commands
| Command | Description |
|---|---|
/plan / /build |
Switch between read-only plan mode and build mode |
/init |
Create or update AGENTS.md |
/review |
Review uncommitted changes, commits, branches, or pull requests |
/explain [project] [target] |
Explain code |
/compact |
Compact the conversation |
/summary |
Append a conversation summary |
/save |
Save the current session |
/sessions |
List saved sessions |
/restore [path|title|--latest|latest] |
Restore a session; title matching uses substring search |
/clear |
Start a fresh conversation |
/model [name] |
Switch LLM profiles; default restores the session's original model |
/exit |
Quit |
Custom commands from prompts/commands/*.md are registered as slash commands as well (TUI only).
Project layout
python_agent_harness/
├── agent.py # Agent FSM core: states, transitions, supervision
├── tool_runner.py # Tool-call execution/delivery + history salvage
├── context_manager.py # Context-ratio tracking + compaction
├── client.py # OpenAI-compatible streaming client (httpx)
├── models.py # Message / ToolCall / ToolSpec data classes
├── token_estimator.py # CJK-aware token estimation + calibration
├── planmode.py # Plan/build modes + plan-file lifecycle
├── prompts.py # Prompt loading + system-prompt assembly
├── persistence.py # Session persistence + titles
├── session.py # Session wiring hub + MCP lifecycle
├── subagent.py # Sub-agent runner + error containment
├── commands.py # Init/review/custom command definitions
├── cli.py # CLI entry points
├── tui/ # Rich + prompt_toolkit TUI (package)
├── diffrender.py # Unified diff generation + Rich rendering
├── mcp/ # Optional MCP client
└── tools/ # Tool implementations + registry
Development
Requires Python ≥ 3.11. CI runs against Python 3.11, 3.12, and 3.13.
make test # unit tests
venv/bin/pip install -e ".[dev]" # development tools
venv/bin/ruff check . # lint
venv/bin/pyright # type checking
venv/bin/python -m build # build sdist + wheel
venv/bin/pip-audit # dependency audit
CI blocks on Ruff and Pyright failures.
Design philosophy
Keep it intact, not bloated.
The project aims to provide a capable coding-agent within a lightweight framework.
Related projects
- gptel-agent-harness — the Emacs-based implementation that inspired this project.
- opencode — the primary source of many prompts and coding-agent behaviors.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file python_agent_harness-1.5.0.tar.gz.
File metadata
- Download URL: python_agent_harness-1.5.0.tar.gz
- Upload date:
- Size: 230.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce97df4ac9aea4f2cce6d7cd46ba8f052dd646d9112595452dfccd370ce40629
|
|
| MD5 |
5da9e1258e8b2b50a943642e2dc8c3e0
|
|
| BLAKE2b-256 |
e58b7897049d6e14ea54bdee70029886543340ba8194798afd1248faa61930e4
|
Provenance
The following attestation bundles were made for python_agent_harness-1.5.0.tar.gz:
Publisher:
release.yml on beacoder/python-agent-harness
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
python_agent_harness-1.5.0.tar.gz -
Subject digest:
ce97df4ac9aea4f2cce6d7cd46ba8f052dd646d9112595452dfccd370ce40629 - Sigstore transparency entry: 2623004588
- Sigstore integration time:
-
Permalink:
beacoder/python-agent-harness@465765d5a35ce4beb8f55b298b5c2d70296573c6 -
Branch / Tag:
refs/tags/v1.5.0 - Owner: https://github.com/beacoder
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@465765d5a35ce4beb8f55b298b5c2d70296573c6 -
Trigger Event:
release
-
Statement type:
File details
Details for the file python_agent_harness-1.5.0-py3-none-any.whl.
File metadata
- Download URL: python_agent_harness-1.5.0-py3-none-any.whl
- Upload date:
- Size: 162.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f4081c01fb49c5167db53279d3639dc2f97ba3090cc876293bc496d1b3f5d8e6
|
|
| MD5 |
c0392add3c4f89e8841c02fabbe5016c
|
|
| BLAKE2b-256 |
7b9f829df24240d4de490b920109b61ac785cea0b4724875d14d79a92946fe87
|
Provenance
The following attestation bundles were made for python_agent_harness-1.5.0-py3-none-any.whl:
Publisher:
release.yml on beacoder/python-agent-harness
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
python_agent_harness-1.5.0-py3-none-any.whl -
Subject digest:
f4081c01fb49c5167db53279d3639dc2f97ba3090cc876293bc496d1b3f5d8e6 - Sigstore transparency entry: 2623004993
- Sigstore integration time:
-
Permalink:
beacoder/python-agent-harness@465765d5a35ce4beb8f55b298b5c2d70296573c6 -
Branch / Tag:
refs/tags/v1.5.0 - Owner: https://github.com/beacoder
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@465765d5a35ce4beb8f55b298b5c2d70296573c6 -
Trigger Event:
release
-
Statement type: