Skip to content

Commit ada2292

Browse files
committed
Rewrite readme.md.
1 parent 8422fca commit ada2292

1 file changed

Lines changed: 103 additions & 133 deletions

File tree

README.md

Lines changed: 103 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,79 @@
1+
<div align="center">
2+
13
# python-agent-harness
24

3-
A Python port of the Emacs [gptel-agent-harness](https://github.com/beacoder/gptel-agent-harness): a minimal-dependency coding agent designed for daily use and easy customization.
5+
**A minimal-dependency coding agent for your terminal** — FSM-driven execution, OpenAI-compatible, made for daily use and easy customization.
46

5-
## Features
7+
[![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)
8+
[![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
9+
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
10+
11+
</div>
12+
13+
A Python port of the Emacs [gptel-agent-harness](https://github.com/beacoder/gptel-agent-harness): a terminal coding agent that 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. No heavy frameworks, no vendor lock-in.
14+
15+
## Demo
616

7-
- **FSM-driven agent execution with completion supervision** — the run is
8-
driven by a finite state machine (WAIT/TOOL/TRET/SUPERVISE/DONE/ERRS/
9-
ABRT); the model is nudged (max 2) when it tries to stop before the task
10-
is complete; the nudge counter resets on tool calls; tool results are
11-
sanitized so a failed call never strands the machine.
12-
- **API retry with backoff** — transient failures (HTTP 429 / 5xx, connection
13-
errors) are retried automatically with exponential backoff + jitter
14-
(honoring `Retry-After`), so a rate limit or a dropped connection no longer
15-
kills the run; retries never duplicate streamed output and a Ctrl-C aborts
16-
the backoff wait promptly. Permanent errors (other 4xx) fail fast.
17-
- **Context management** — CJK-aware token estimation, per-model context
18-
windows (deepseek-v4/glm-5.2 1M, gpt-5 400k, kimi-k2.7 256k, claude 200k,
19-
...), self-calibrating estimates from API-reported input tokens, and
20-
automatic compaction at 70% usage that summarizes the conversation and
21-
resumes with the last user request.
22-
- **Tools** — Agent (sub-agents), TodoWrite, Glob (git-aware), Grep
23-
(git grep → rg → grep), Read, Insert, Edit (incl. unified diffs), Write,
24-
Mkdir, Bash, Skill, Question, and PlanExit (registered while in plan
25-
mode) — all OpenAI-compatible tool schemas. Tool execution mirrors
26-
gptel's `gptel--handle-tool-use`: synchronous tools (Read, Edit,
27-
Glob, ...) run ONE AT A TIME in model-emitted order, while the
28-
asynchronous tools (Bash, Agent) are dispatched and run concurrently
29-
in the background — results are delivered in the original call order.
30-
- **Default agent prompts** — the main agent and sub-agents each get a
31-
distinct default system prompt bundled with the package
32-
(`prompts/agent.md`, `prompts/subagent.md`), with YAML frontmatter
33-
stripped and the `{{SKILLS}}` placeholder filled from the discovered
34-
skill directory. The main prompt is prefixed with the project context
35-
files and the task-completion rules; sub-agents get only their own
36-
prompt. `/init` `/review` `/explain` and custom commands
37-
(`prompts/commands/*.md`) run with their own prompt for that run.
38-
- **Plan / Build modes** — plan mode is read-only except the per-session
39-
plan file; PlanExit switches back to build with an "execute the plan"
40-
prompt; sub-agents in plan mode receive the read-only reminder.
41-
- **Sessions** — auto-saved after every response to
42-
`~/.local/share/python-agent-harness/sessions/`, LLM-generated titles
43-
(one-shot per session, fired when the agent run finishes; the file is
44-
renamed to `<title>_<TS>.md`), `/restore` (with `--latest`) and
45-
`/sessions` TUI commands.
46-
- **Commands**`/init` (create/update AGENTS.md), `/review` (uncommitted
47-
changes / commit / branch / PR), `/summary`, `/explain` and custom
48-
commands from `prompts/commands/*.md` — all TUI slash commands.
49-
Tool availability: `/init`/`/review` may use **all
50-
tools except PlanExit** (the PlanExit tool is hidden for the run,
51-
including for spawned sub-agents); custom commands may use all tools
52-
including PlanExit; `compact`/`summary` run with **no tools** (a
53-
one-shot `chat_sync` call, like session-title generation).
54-
- **Editing input**`prompt_toolkit`-backed multi-line editor with
55-
persistent history (Up/Down recall), Enter for a newline, Esc+Enter
56-
(or Alt+Enter) to submit, and **Tab completion** (Tab to complete,
57-
Shift+Tab to cycle backwards): the first token starting with `/`
58-
completes against the slash commands (builtins + custom
59-
`prompts/commands/*.md`); after a slash command's space, Tab
60-
completes paths relative to the project dir (absolute and `~` paths
61-
work too; directories get a trailing `/` to keep drilling). In plain
62-
messages, any token containing `/` or starting with `~` (e.g.
63-
`~/wor`, `docs/`) completes as a path the same way — `~` against
64-
`$HOME`, otherwise relative to the project dir — so `~/wor` + Tab
65-
becomes `~/workspace/`.
66-
- **TUI** — rich live interface with a pinned status bar (mode, context
67-
usage, spinner), streaming assistant output, tool-result previews, a
68-
pinned Todos panel (sub-agent lists shown with a `sub:` label), and
69-
numbered-choice questions for Question tool / PlanExit confirmations.
70-
- **Diff rendering** — Edit/Write tool calls capture a unified diff of
71-
the file change and render it inline (red/green) in the TUI, so file
72-
edits are visible without leaving the app.
73-
74-
## Screenshot
75-
76-
![demo.png](demo.png)
77-
78-
## Install
17+
![Demo](demo.png)
18+
19+
## Quick start
7920

8021
```sh
81-
python -m venv venv
82-
venv/bin/pip install rich httpx prompt_toolkit
83-
venv/bin/pip install -e python-agent-harness
22+
git clone git@github.com:beacoder/python-agent-harness.git
23+
cd python-agent-harness
24+
make install # create venv, install deps + package
25+
. venv/bin/activate # add venv/bin to PATH
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
8428
```
8529

86-
## Configuration
87-
88-
LLM settings live in a JSON config file — no environment variables needed:
30+
Edit `~/.config/python-agent-harness/config.json`, set your `base_url`, `api_key` and `model`, and you're ready to go:
8931

9032
```sh
91-
python-agent-harness config --init # write ~/.config/python-agent-harness/config.json
92-
python-agent-harness config # show effective settings (API key masked)
33+
python-agent-harness run ~/my-project
9334
```
9435

95-
Edit `~/.config/python-agent-harness/config.json`:
36+
## Features
37+
38+
### Reliable by design
39+
40+
- **FSM-driven execution with completion supervision** — the run is driven by a finite state machine (`WAIT`/`TOOL`/`TRET`/`SUPERVISE`/`DONE`/`ERRS`/`ABRT`). When the model tries to stop before the task is done, it is nudged (max 2); the nudge counter resets on tool calls, and tool results are sanitized so a failed call never strands the machine.
41+
- **Automatic retry with backoff** — transient failures (HTTP 429 / 5xx, connection errors) retry with exponential backoff + jitter, honoring `Retry-After`; retries never duplicate streamed output, Ctrl-C aborts the wait promptly, and permanent 4xx errors fail fast.
42+
43+
### Long conversations, no babysitting
44+
45+
- **Context management** — CJK-aware token estimation, per-model context windows (deepseek-v4/glm-5.2 1M, gpt-5 400k, kimi-k2.7 256k, claude 200k, ...), self-calibrating estimates from API-reported input tokens, and automatic compaction at 70% usage that summarizes the conversation and resumes with the last user request.
46+
47+
### Real coding tools
48+
49+
All OpenAI-compatible tool schemas: **Agent** (sub-agents), **TodoWrite**, **Glob** (git-aware), **Grep** (git grep → rg → grep), **Read**, **Insert**, **Edit** (incl. unified diffs), **Write**, **Mkdir**, **Bash**, **Skill**, **Question**, and **PlanExit** (plan mode only).
50+
51+
Tool execution mirrors gptel's `gptel--handle-tool-use`: synchronous tools (Read, Edit, Glob, ...) run one at a time in model-emitted order, while asynchronous tools (Bash, Agent) run concurrently in the background — results delivered in original call order.
52+
53+
### Plan before you build
54+
55+
- **Plan / Build modes** — plan mode is read-only except the per-session plan file; `PlanExit` switches back to build with an "execute the plan" prompt; sub-agents in plan mode get the read-only reminder.
56+
57+
### Sessions that survive
58+
59+
- Auto-saved after every response to `~/.local/share/python-agent-harness/sessions/`
60+
- LLM-generated titles (one-shot, when the run finishes; file renamed to `<title>_<TS>.md`)
61+
- `/restore` (with `--latest`) and `/sessions` TUI commands
62+
63+
### A TUI built for focus
64+
65+
- Rich live interface: pinned status bar (mode, context usage, spinner), streaming output, tool-result previews, pinned Todos panel (sub-agent lists labeled `sub:`), and numbered-choice prompts for Question / PlanExit.
66+
- **Inline diff rendering** — Edit/Write calls capture a unified diff and render it red/green in the TUI, so file changes are visible without leaving the app.
67+
- `prompt_toolkit` editor: Enter for newline, **Esc+Enter** (or Alt+Enter) to submit, **Tab completion** for slash commands and paths (`~/` and relative, Shift+Tab to cycle backwards), Up/Down history recall (persisted to `~/.local/share/python-agent-harness/input_history`), **Ctrl-D** quits, **Ctrl-C** cancels the current input or run without leaving the app — history is preserved so you can immediately ask a follow-up, and a cancelled worker can never clobber the next run's state (per-run cancellation identity).
68+
69+
### Extensible
70+
71+
- **Default agent prompts** — distinct system prompts for the main agent and sub-agents (`prompts/agent.md`, `prompts/subagent.md`), YAML frontmatter stripped, `{{SKILLS}}` filled from the discovered skill directory; the main prompt is prefixed with project context files and task-completion rules.
72+
- **Slash commands**`/init`, `/review`, `/explain`, plus custom commands from `prompts/commands/*.md` become TUI slash commands automatically.
73+
74+
## Configuration
75+
76+
LLM settings live in a JSON config file — no environment variables required:
9677

9778
```json
9879
{
@@ -120,58 +101,38 @@ Edit `~/.config/python-agent-harness/config.json`:
120101
}
121102
```
122103

123-
`reasoning_effort` is passed to the API as-is (omitted when unset), so you
124-
can use whatever your provider accepts ("low"/"medium"/"high" for OpenAI
125-
and compatible providers). Other optional keys: `backend`, `temperature`,
126-
`max_tokens`, `timeout`, `stream` (`true` by default; set `false` for
127-
non-streaming one-shot responses — `python-agent-harness run --no-stream`
128-
overrides it on the command line). The `paths` object (`context_path`,
129-
`skill_path`) overrides the context/skill directory discovery — defaults
130-
are `<project>/contexts` or `~/.emacs.d/contexts` for context files, and
131-
`<project>/skills` or `~/.emacs.d/skills` for skills.
132-
133-
`subagent_llm` configures the LLM used for sub-agent (Agent tool)
134-
requests, mirroring `gptel-agent-harness-subagent-model`/`-backend`: every
135-
key is optional and an unset key inherits the main `llm` settings, so a
136-
cheaper/smaller model (or even a different provider via `base_url` +
137-
`api_key`) can serve delegated work. When no key differs from the main
138-
`llm`, the sub-agent shares the main client.
139-
140-
Precedence: code defaults < config file < `OPENAI_*` environment variables
141-
(env still wins if you set them, but nothing is required). Sub-agent
142-
settings additionally honor `OPENAI_SUBAGENT_*` variables
143-
(`OPENAI_SUBAGENT_BASE_URL`, `OPENAI_SUBAGENT_API_KEY`,
144-
`OPENAI_SUBAGENT_MODEL`, `OPENAI_SUBAGENT_BACKEND`). Use a custom
145-
file with `--config PATH` (also settable via `PYTHON_AGENT_HARNESS_CONFIG`).
146-
147-
LLM request/response bodies are logged as JSON to
148-
`/tmp/python-agent-harness-<date>-<id>.json` (override the directory with
149-
`LLM_LOG_DIR`); the path is printed at TUI startup.
104+
- `reasoning_effort` is passed to the API as-is (omitted when unset) — whatever your provider accepts ("low"/"medium"/"high").
105+
- Other optional keys: `backend`, `temperature`, `max_tokens`, `timeout`, `stream` (`true` by default; `run --no-stream` overrides on the command line).
106+
- `subagent_llm` configures the LLM for Agent-tool requests: every key is optional and unset keys inherit the main `llm`, so a cheaper/smaller model (or a different provider) can serve delegated work.
107+
- `paths.context_path` / `paths.skill_path` override context/skill discovery — defaults are `<project>/contexts` or `~/.emacs.d/contexts` (skills: `<project>/skills` or `~/.emacs.d/skills`).
108+
- Precedence: code defaults < config file < `OPENAI_*` env vars (env still wins if set, but nothing is required). Sub-agent settings honor `OPENAI_SUBAGENT_*` (`_BASE_URL`, `_API_KEY`, `_MODEL`, `_BACKEND`).
109+
- Use a custom config with `--config PATH` (or `PYTHON_AGENT_HARNESS_CONFIG`).
110+
111+
LLM request/response bodies are logged as JSON to `/tmp/python-agent-harness-<date>-<id>.json` (override the directory with `LLM_LOG_DIR`); the path is printed at TUI startup.
150112

151113
## Usage
152114

153115
```sh
154116
python-agent-harness run [project-dir] # interactive TUI agent
155117
```
156118

157-
TUI slash commands: `/plan` `/build` `/init` `/review` `/explain`
158-
`/compact` `/save` `/summary` `/sessions`
159-
`/restore` `/clear` `/exit``/explain [project] [target]` explains
160-
code and `/summary` appends a conversation summary (both TUI-only);
161-
`/sessions` lists saved sessions and `/restore [path|title|--latest]`
162-
restores one (`/restore` matches sessions by title substring). Custom
163-
commands from `prompts/commands/*.md` are TUI slash commands too
164-
(TUI-only — no CLI subcommand is registered for them).
165-
166-
Input editing: type your message, press **Enter** for a new line, and
167-
**Esc then Enter** (or **Alt+Enter**) to submit. **Up/Down** recall
168-
previous inputs from `~/.local/share/python-agent-harness/input_history`.
169-
**Ctrl-D** quits; **Ctrl-C** cancels the current input or agent run
170-
without leaving the app — the conversation history is preserved, so you
171-
can immediately ask a follow-up question; a cancelled worker can never
172-
clobber the next run's state (per-run cancellation identity).
173-
174-
## Layout
119+
| Command | What it does |
120+
|---|---|
121+
| `/plan` / `/build` | switch between read-only plan and build mode |
122+
| `/init` | create/update `AGENTS.md` |
123+
| `/review` | review uncommitted changes / commit / branch / PR |
124+
| `/explain [project] [target]` | explain code |
125+
| `/compact` | compact the conversation |
126+
| `/summary` | append a conversation summary |
127+
| `/save` | save the session |
128+
| `/sessions` | list saved sessions |
129+
| `/restore [path\|title\|--latest]` | restore a session (title substring match) |
130+
| `/clear` | start a fresh conversation |
131+
| `/exit` | quit |
132+
133+
Custom commands from `prompts/commands/*.md` are registered as slash commands too (TUI-only — no CLI subcommand is registered for them). Tool availability differs per command: `/init` and `/review` may use all tools except `PlanExit` (hidden for the run, including for spawned sub-agents); custom commands may use everything; `compact`/`summary` run with no tools (a one-shot `chat_sync` call, like session-title generation).
134+
135+
## Project layout
175136

176137
```
177138
python_agent_harness/
@@ -191,15 +152,20 @@ python_agent_harness/
191152
└── tools/ tool implementations + registry
192153
```
193154

194-
## Tests
155+
## Development
156+
157+
Python ≥ 3.11 required (CI runs 3.11 / 3.12 / 3.13).
195158

196159
```sh
197-
venv/bin/python -m unittest discover -s tests -v
160+
make test # unit tests (unittest discover)
161+
venv/bin/pip install -e ".[dev]" # dev tools: ruff, pyright, build, pip-audit
162+
venv/bin/ruff check . # lint (CI blocks on this)
163+
venv/bin/pyright # type check, basic mode (CI blocks on this)
164+
venv/bin/python -m build # sdist + wheel
165+
venv/bin/pip-audit # dependency audit
198166
```
199167

200-
Python ≥ 3.11 is required (CI runs 3.11 / 3.12 / 3.13).
201-
202-
## Verification checklist (ported semantics)
168+
### Verification checklist (ported semantics)
203169

204170
- [x] Nudge supervision with fail-closed dead-session budget
205171
- [x] Tool-result sanitization (None → error placeholder)
@@ -209,3 +175,7 @@ Python ≥ 3.11 is required (CI runs 3.11 / 3.12 / 3.13).
209175
- [x] Session metadata round-trip and title sanitization
210176
- [x] One-shot LLM title generation after the agent run finishes
211177
- [x] Ctrl-C cancel: stale workers can't clobber the next run's history
178+
179+
## License
180+
181+
MIT

0 commit comments

Comments
 (0)