Skip to content

Commit 18244ab

Browse files
authored
Update README.md
1 parent 9e891ec commit 18244ab

1 file changed

Lines changed: 140 additions & 79 deletions

File tree

‎README.md‎

Lines changed: 140 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -2,66 +2,86 @@
22

33
# python-agent-harness
44

5-
**Lightweight Python coding-agent harness for building reliable autonomous coding agents.** — FSM-driven execution, OpenAI-compatible, made for daily use and easy customization.
5+
**A lightweight Python coding-agent harness for building reliable autonomous coding agents.**
6+
FSM-driven execution · OpenAI-compatible · built for daily use and easy customization
67

78
[![CI](https://github.com/beacoder/python-agent-harness/actions/workflows/ci.yml/badge.svg)](https://github.com/beacoder/python-agent-harness/actions/workflows/ci.yml)
89
[![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
910
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
1011

1112
</div>
1213

13-
A terminal coding agent inspired by [gptel-agent-harness](https://github.com/beacoder/gptel-agent-harness) and [opencode](https://github.com/anomalyco/opencode): reads your repo, plans, edits files, runs commands, and verifies its own work — with only **three runtime dependencies** (`rich`, `httpx`, `prompt_toolkit`) and any OpenAI-compatible API. It ports opencode's prompts and behaviors (AGENTS.md discovery, plan/build modes, skills, sub-agents, todo tracking) while staying dependency-light.
14+
A terminal coding agent that reads your codebase, plans changes, edits files, runs commands, and verifies its work.
15+
16+
`python-agent-harness` is inspired by [gptel-agent-harness](https://github.com/beacoder/gptel-agent-harness) and [opencode](https://github.com/anomalyco/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**:
17+
18+
- `rich`
19+
- `httpx`
20+
- `prompt_toolkit`
21+
22+
It works with any **OpenAI-compatible API** and is designed to be easy to inspect, customize, and use for everyday software development.
1423

1524
## Demo
1625

17-
![Demo](demo.png)
26+
![python-agent-harness demo](demo.png)
1827

1928
## Quick start
2029

2130
```sh
2231
git clone git@github.com:beacoder/python-agent-harness.git
2332
cd python-agent-harness
24-
make install # create venv, install deps + package
33+
34+
make install
2535
. venv/bin/activate
26-
python-agent-harness config --init # write ~/.config/python-agent-harness/config.json
27-
python-agent-harness run # launch the agent in your project dir
36+
37+
python-agent-harness config --init
38+
python-agent-harness run
2839
```
2940

30-
Edit `~/.config/python-agent-harness/config.json`, set `base_url`/`api_key`/`model`, and run. Optional: `pip install -e ".[mcp]"` for MCP server integration; `pip install -e ".[dev]"` for dev tooling.
41+
Edit `~/.config/python-agent-harness/config.json` and set your `base_url`, `api_key`, and `model`.
42+
43+
Optional extras:
44+
45+
```sh
46+
pip install -e ".[mcp]" # MCP server integration
47+
pip install -e ".[dev]" # development tools
48+
```
3149

3250
## Features
3351

34-
- **FSM-driven execution** (`WAIT`/`TOOL`/`TRET`/`SUPERVISE`/`DONE`/`ERRS`/`ABRT`) with completion supervision: the model is nudged (max 2) if it stops early; failed tool calls are sanitized and never strand the machine. Transient failures (429/5xx) retry with exponential backoff + jitter.
35-
- **Context management** — CJK-aware token estimation, per-model context windows, automatic compaction at 70% usage.
36-
- **Real coding tools** — Agent (sub-agents), TodoWrite, Glob, Grep, Read, Insert, Edit (incl. unified diffs), Write, Mkdir, Bash, Skill, Question, PlanExit. Synchronous tools run one at a time; asynchronous ones (Bash, Agent) run concurrently in emitted order.
37-
- **Plan / Build modes** — plan mode is read-only except the per-session plan file.
38-
- **Sessions that survive** — auto-saved to `~/.local/share/python-agent-harness/sessions/` after every response, LLM-generated titles, `/restore --latest`, `/sessions`.
39-
- **A TUI built for focus** — rich live interface with pinned status bar, Todos panel, inline red/green diff rendering for Edit/Write, `prompt_toolkit` editor (Esc+Enter to submit, Tab completion, history, Ctrl-D quits, Ctrl-C cancels without leaving the app).
40-
- **MCP servers (optional)** — with the `[mcp]` extra, MCP tools become ordinary agent tools (`mcp__<server>__<tool>`); supports `stdio`, `streamable-http`, `sse` transports.
41-
- **Slash commands** — `/init`, `/review`, `/explain`, plus custom commands from `prompts/commands/*.md`.
52+
- **FSM-driven execution** — explicit `WAIT` / `TOOL` / `TRET` / `SUPERVISE` / `DONE` / `ERRS` / `ABRT` states. 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.
53+
- **Context management** — CJK-aware token estimation, per-model context windows, and automatic compaction at 70% usage.
54+
- **Coding tools** — `Agent`, `TodoWrite`, `Glob`, `Grep`, `Read`, `Insert`, `Edit` (including unified diffs), `Write`, `Mkdir`, `Bash`, `Skill`, `Question`, and `PlanExit`. Synchronous tools execute sequentially; asynchronous tools such as `Bash` and `Agent` can run concurrently while preserving emitted order.
55+
- **Plan / Build modes** — plan mode is read-only except for the per-session plan file.
56+
- **Persistent sessions** — sessions are automatically saved after every response to `~/.local/share/python-agent-harness/sessions/`, with LLM-generated titles and support for `/restore --latest` and `/sessions`.
57+
- **Focused TUI** — a Rich-based interface with a pinned status bar, Todos panel, inline red/green diff rendering for `Edit` and `Write`, and a `prompt_toolkit` editor with history and completion. `Esc+Enter` submits, `Ctrl-D` quits, and `Ctrl-C` cancels without leaving the application.
58+
- **MCP support** — optional MCP integration through the `[mcp]` extra. MCP tools become ordinary agent tools such as `mcp__<server>__<tool>`. Supports `stdio`, `streamable-http`, and `sse` transports.
59+
- **Slash commands** — built-in `/init`, `/review`, `/explain`, and other commands, plus custom commands loaded from `prompts/commands/*.md`.
4260

43-
## Mini opencode
61+
## Inspired by opencode
4462

45-
Most of [opencode](https://github.com/anomalyco/opencode)'s prompts and behaviors have been ported over, so the agent reasons and works like opencode while staying dependency-light.
63+
Most of [opencode](https://github.com/anomalyco/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.
4664

47-
**Prompts extracted from opencode** (in `python_agent_harness/prompts/`):
65+
### Prompt and behavior mapping
4866

49-
| opencode prompt | harness equivalent |
67+
The following opencode prompts have corresponding implementations in `python-agent-harness`:
68+
69+
| opencode | python-agent-harness |
5070
|---|---|
5171
| `default.txt` (main agent) | `agent.md` |
5272
| `plan.txt` / `plan-mode.txt` / `build-switch.txt` | `plan.md` / `plan-mode.md` / `build-switch.md` |
53-
| `task.txt` (subagent) | `subagent.md` + `Agent` tool |
73+
| `task.txt` (sub-agent) | `subagent.md` + `Agent` tool |
5474
| `todowrite.txt` / `question.txt` / `skill.txt` | `TodoWrite` / `Question` / `Skill` tools |
5575
| `read.txt` / `write.txt` / `edit.txt` / `grep.txt` / `glob.txt` | `Read` / `Write` / `Edit` / `Grep` / `Glob` tools |
56-
| `shell.txt` (git/bash guidance) | `Bash` tool + `agent.md` "Git and GitHub" section |
76+
| `shell.txt` | `Bash` tool + `agent.md` Git/GitHub guidance |
5777
| `plan-enter.txt` / `plan-exit.txt` | `PlanExit` tool |
58-
| `initialize.txt` / `review.txt` / `explain` commands | `initialize.md` / `review.md` / `commands/explain.md` |
78+
| `initialize.txt` / `review.txt` / `explain` | `initialize.md` / `review.md` / `commands/explain.md` |
5979
| compaction / summary / title | `compact.md` / `summary.md` / `title.md` |
60-
| AGENTS.md handling | `prompts.py` (`find_agents_md_files`, `load_context_files`, per-file resolution) |
80+
| `AGENTS.md` handling | `prompts.py` (`find_agents_md_files`, `load_context_files`, per-file resolution) |
6181

6282
## Configuration
6383

64-
All LLM settings live in one JSON config file (no env vars required):
84+
All LLM settings live in a single JSON configuration file. Environment variables are optional.
6585

6686
```json
6787
{
@@ -74,93 +94,134 @@ All LLM settings live in one JSON config file (no env vars required):
7494
},
7595
"models": {
7696
"_comment": "Named LLM profiles for /model switching. Partial settings; unset keys inherit the main llm.",
77-
"deepseek": { "base_url": "https://api.deepseek.com/v1", "model": "deepseek-chat" },
78-
"qwen": { "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", "model": "qwen3.5-coder" }
97+
"deepseek": {
98+
"base_url": "https://api.deepseek.com/v1",
99+
"model": "deepseek-chat"
100+
},
101+
"qwen": {
102+
"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
103+
"model": "qwen3.5-coder"
104+
}
79105
},
80106
"subagent_llm": {
81107
"profile": null,
82-
"base_url": null, "api_key": null, "model": null,
83-
"temperature": null, "max_tokens": null, "timeout": null,
84-
"reasoning_effort": null, "stream": null
108+
"base_url": null,
109+
"api_key": null,
110+
"model": null,
111+
"temperature": null,
112+
"max_tokens": null,
113+
"timeout": null,
114+
"reasoning_effort": null,
115+
"stream": null
116+
},
117+
"paths": {
118+
"context_path": null,
119+
"skill_path": null
85120
},
86-
"paths": { "context_path": null, "skill_path": null },
87121
"mcp": {
88122
"servers": {
89-
"example": { "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"], "env": [], "parallel": false, "timeout": null, "enabled": false }
123+
"example": {
124+
"transport": "stdio",
125+
"command": "npx",
126+
"args": [
127+
"-y",
128+
"@modelcontextprotocol/server-filesystem",
129+
"/tmp"
130+
],
131+
"env": [],
132+
"parallel": false,
133+
"timeout": null,
134+
"enabled": false
135+
}
90136
}
91137
}
92138
}
93139
```
94140

95-
- Optional `llm` keys: `backend`, `temperature`, `max_tokens`, `timeout`, `reasoning_effort` (passed to the API as-is when set), `stream` (`run --no-stream` overrides).
96-
- **`models`** — named LLM profiles for runtime switching via `/model` (in the TUI: no arg lists, name or number switches; `default` always restores the main `llm` settings). Each profile is a partial settings dict; unset keys inherit the main `llm`.
97-
- **`subagent_llm`** — LLM for Agent-tool requests; every key optional, unset keys inherit main `llm`. Set `profile` to a name from `models` to reuse a profile; precedence: profile settings > explicit `subagent_llm` keys > main `llm` > env.
98-
- **`paths.context_path` / `paths.skill_path`** — where to load context files / skills from. Unset means the project's own `<project>/contexts` and `<project>/skills`.
99-
- **`mcp.servers`** — requires the `[mcp]` extra; each server is `{transport, command, args, env, url, headers, parallel, timeout, enabled}`.
100-
- **Precedence**: code defaults < config file < `OPENAI_*` env vars. Sub-agent settings honor `OPENAI_SUBAGENT_*` (`_BASE_URL`, `_API_KEY`, `_MODEL`, `_BACKEND`).
101-
- Custom config: `--config PATH` or `PYTHON_AGENT_HARNESS_CONFIG`.
102-
- LLM request/response bodies are logged as JSON to `/tmp/python-agent-harness-<date>-<id>.json` (override dir with `LLM_LOG_DIR`); path printed at startup.
141+
### Configuration options
142+
143+
- **`llm`** — main LLM configuration. Optional keys include `backend`, `temperature`, `max_tokens`, `timeout`, `reasoning_effort`, and `stream`. Values such as `reasoning_effort` are passed to the API as-is when set. `run --no-stream` overrides `stream`.
144+
- **`models`** — named LLM profiles for runtime switching with `/model`. A profile is a partial settings dictionary; unset keys inherit from the main `llm`. `default` restores the main LLM configuration.
145+
- **`subagent_llm`** — LLM configuration for `Agent` tool requests. Unset values inherit from the main `llm`. Set `profile` to reuse a profile from `models`. Precedence is: profile settings > explicit `subagent_llm` settings > main `llm` > environment variables.
146+
- **`paths.context_path` / `paths.skill_path`** — locations from which to load context files and skills. When unset, the project-local `<project>/contexts` and `<project>/skills` directories are used.
147+
- **`mcp.servers`** — MCP server configuration. Requires the `[mcp]` extra. Each server supports `transport`, `command`, `args`, `env`, `url`, `headers`, `parallel`, `timeout`, and `enabled`.
148+
- **Configuration precedence** — code defaults < config file < `OPENAI_*` environment variables. Sub-agent settings also support `OPENAI_SUBAGENT_*` (`_BASE_URL`, `_API_KEY`, `_MODEL`, `_BACKEND`).
149+
- **Custom config** — use `--config PATH` or `PYTHON_AGENT_HARNESS_CONFIG`.
150+
- **LLM logging** — request and response bodies are logged as JSON to `/tmp/python-agent-harness-<date>-<id>.json`. Set `LLM_LOG_DIR` to change the directory. The log path is printed at startup.
103151

104152
## Usage
105153

106154
```sh
107-
python-agent-harness run [project-dir] # interactive TUI agent
155+
python-agent-harness run [project-dir]
108156
```
109157

110-
| Command | What it does |
158+
Launches the interactive TUI agent. If `project-dir` is omitted, the current directory is used.
159+
160+
### Slash commands
161+
162+
| Command | Description |
111163
|---|---|
112-
| `/plan` / `/build` | switch between read-only plan and build mode |
113-
| `/init` | create/update `AGENTS.md` |
114-
| `/review` | review uncommitted changes / commit / branch / PR |
115-
| `/explain [project] [target]` | explain code |
116-
| `/compact` | compact the conversation |
117-
| `/summary` | append a conversation summary |
118-
| `/save` | save the session |
119-
| `/sessions` | list saved sessions |
120-
| `/restore [path\|title\|--latest\|latest]` | restore a session (title substring match) |
121-
| `/clear` | start a fresh conversation |
122-
| `/model [name]` | switch LLM model profile (`default` restores the session's original model; no arg: list available) |
123-
| `/exit` | quit |
124-
125-
Custom commands from `prompts/commands/*.md` are registered as slash commands too (TUI-only).
164+
| `/plan` / `/build` | Switch between read-only plan mode and build mode |
165+
| `/init` | Create or update `AGENTS.md` |
166+
| `/review` | Review uncommitted changes, commits, branches, or pull requests |
167+
| `/explain [project] [target]` | Explain code |
168+
| `/compact` | Compact the conversation |
169+
| `/summary` | Append a conversation summary |
170+
| `/save` | Save the current session |
171+
| `/sessions` | List saved sessions |
172+
| `/restore [path\|title\|--latest\|latest]` | Restore a session; title matching uses substring search |
173+
| `/clear` | Start a fresh conversation |
174+
| `/model [name]` | Switch LLM profiles; `default` restores the session's original model |
175+
| `/exit` | Quit |
176+
177+
Custom commands from `prompts/commands/*.md` are registered as slash commands as well (TUI only).
126178

127179
## Project layout
128180

129-
```
181+
```text
130182
python_agent_harness/
131-
├── agent.py agent FSM (states, transitions, supervision, compaction)
132-
├── client.py OpenAI-compatible streaming client (httpx)
133-
├── models.py Message / ToolCall / ToolSpec data classes
134-
├── token_estimator.py CJK-aware token estimation + calibration
135-
├── planmode.py build/plan mode + plan file lifecycle
136-
├── prompts.py prompt loading + system prompt assembly
137-
├── session_store.py session persistence + titles
138-
├── agent_session.py AgentSession (wiring hub; MCP lifecycle)
139-
├── subagent.py sub-agent runner (error containment, plan reminder)
140-
├── commands.py init/review/custom command definitions
141-
├── cli.py argparse entry points
142-
├── tui.py rich + prompt_toolkit TUI
143-
├── diffrender.py unified diff generation + rich rendering
144-
├── mcp/ optional MCP client (config, SDK wrapper, manager)
145-
└── tools/ tool implementations + registry (incl. MCPTool adapter)
183+
├── agent.py # Agent FSM: states, transitions, supervision, compaction
184+
├── client.py # OpenAI-compatible streaming client (httpx)
185+
├── models.py # Message / ToolCall / ToolSpec data classes
186+
├── token_estimator.py # CJK-aware token estimation + calibration
187+
├── planmode.py # Plan/build modes + plan-file lifecycle
188+
├── prompts.py # Prompt loading + system-prompt assembly
189+
├── session_store.py # Session persistence + titles
190+
├── agent_session.py # AgentSession wiring hub + MCP lifecycle
191+
├── subagent.py # Sub-agent runner + error containment
192+
├── commands.py # Init/review/custom command definitions
193+
├── cli.py # CLI entry points
194+
├── tui.py # Rich + prompt_toolkit TUI
195+
├── diffrender.py # Unified diff generation + Rich rendering
196+
├── mcp/ # Optional MCP client
197+
└── tools/ # Tool implementations + registry
146198
```
147199

148200
## Development
149201

150-
Python ≥ 3.11 (CI runs 3.11 / 3.12 / 3.13).
202+
Requires Python ≥ 3.11. CI runs against Python 3.11, 3.12, and 3.13.
151203

152204
```sh
153-
make test # unit tests (unittest discover)
154-
venv/bin/pip install -e ".[dev]" # dev tools: ruff, pyright, build, pip-audit
155-
venv/bin/ruff check . # lint (CI blocks on this)
156-
venv/bin/pyright # type check, basic mode (CI blocks on this)
157-
venv/bin/python -m build # sdist + wheel
205+
make test # unit tests
206+
venv/bin/pip install -e ".[dev]" # development tools
207+
venv/bin/ruff check . # lint
208+
venv/bin/pyright # type checking
209+
venv/bin/python -m build # build sdist + wheel
158210
venv/bin/pip-audit # dependency audit
159211
```
160212

161-
## Principle
213+
CI blocks on Ruff and Pyright failures.
214+
215+
## Design principle
216+
217+
**Keep it intact, not bloated.**
218+
219+
The project aims to provide a capable coding-agent runtime without hiding the core agent loop behind a heavyweight framework.
220+
221+
## Related projects
162222

163-
Keep it intact, not bloated.
223+
- [gptel-agent-harness](https://github.com/beacoder/gptel-agent-harness) — the Emacs-based implementation that inspired this project.
224+
- [opencode](https://github.com/anomalyco/opencode) — the primary source of many prompts and coding-agent behaviors.
164225

165226
## License
166227

0 commit comments

Comments
 (0)