Skip to content

Commit 2beb3fc

Browse files
committed
Update readme.md
1 parent ba1a7f6 commit 2beb3fc

1 file changed

Lines changed: 33 additions & 106 deletions

File tree

README.md

Lines changed: 33 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
</div>
1212

13-
Python coding-agent inspired by [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.
13+
A terminal coding agent inspired by [gptel-agent-harness](https://github.com/beacoder/gptel-agent-harness): 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.
1414

1515
## Demo
1616

@@ -22,61 +22,27 @@ Python coding-agent inspired by [gptel-agent-harness](https://github.com/beacode
2222
git clone git@github.com:beacoder/python-agent-harness.git
2323
cd python-agent-harness
2424
make install # create venv, install deps + package
25-
. venv/bin/activate # add venv/bin to PATH
25+
. venv/bin/activate
2626
python-agent-harness config --init # write ~/.config/python-agent-harness/config.json
2727
python-agent-harness run # launch the agent in your project dir
2828
```
2929

30-
Optional extras: `pip install -e ".[mcp]"` enables MCP server integration (see below); `pip install -e ".[dev]"` installs the development tooling.
31-
32-
Edit `~/.config/python-agent-harness/config.json`, set your `base_url`, `api_key` and `model`, and you're ready to go:
33-
34-
```sh
35-
python-agent-harness run ~/my-project
36-
```
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.
3731

3832
## Features
3933

40-
### Reliable by design
41-
42-
- **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.
43-
- **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.
44-
45-
### Long conversations, no babysitting
46-
47-
- **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.
48-
49-
### Real coding tools
50-
51-
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).
52-
53-
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.
54-
55-
### Plan before you build
56-
57-
- **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.
58-
59-
### Sessions that survive
60-
61-
- Auto-saved after every response to `~/.local/share/python-agent-harness/sessions/`
62-
- LLM-generated titles (one-shot, when the run finishes; file renamed to `<title>_<TS>.md`)
63-
- `/restore` (with `--latest`) and `/sessions` TUI commands
64-
65-
### A TUI built for focus
66-
67-
- 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.
68-
- **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.
69-
- `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).
70-
71-
### Extensible
72-
73-
- **MCP servers (optional)** — with `pip install -e ".[mcp]"`, any MCP server's tools become ordinary agent tools, namespaced `mcp__<server>__<tool>` (so `search` from two servers never collides). Supported transports: `stdio` (spawn a command), `streamable-http` and `sse` (remote URLs). Tool discovery happens once at session start; results are normalized into the harness's tool-result format, MCP errors surface as normal tool errors, and `parallel: true` opts a read-only server into concurrent execution (serial by default). Configured in the `mcp` section of the config file (or programmatically via `MCPConfig` + `session.connect_mcp()`); `enabled: false` keeps an entry without connecting it. The `mcp` extra is never required — the base harness works without it.
74-
- **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.
75-
- **Slash commands**`/init`, `/review`, `/explain`, plus custom commands from `prompts/commands/*.md` become TUI slash commands automatically.
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 (summarizes and resumes with the last user request).
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`.
7642

7743
## Configuration
7844

79-
LLM settings live in a JSON config file no environment variables required:
45+
All LLM settings live in one JSON config file (no env vars required):
8046

8147
```json
8248
{
@@ -88,61 +54,33 @@ LLM settings live in a JSON config file — no environment variables required:
8854
"stream": true
8955
},
9056
"models": {
91-
"_comment": "Named LLM profiles for /model switching. Each entry is a partial set of LLM settings; unset keys inherit the main llm.",
92-
"deepseek": {
93-
"base_url": "https://api.deepseek.com/v1",
94-
"model": "deepseek-chat",
95-
"reasoning_effort": "medium"
96-
},
97-
"qwen": {
98-
"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
99-
"model": "qwen3.5-coder"
100-
},
101-
"kimi": {
102-
"base_url": "https://api.moonshot.cn/v1",
103-
"model": "kimi-k2.7"
104-
}
57+
"_comment": "Named LLM profiles for /model switching. Partial settings; unset keys inherit the main llm.",
58+
"deepseek": { "base_url": "https://api.deepseek.com/v1", "model": "deepseek-chat" },
59+
"qwen": { "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", "model": "qwen3.5-coder" }
10560
},
10661
"subagent_llm": {
107-
"base_url": null,
108-
"api_key": null,
109-
"model": null,
110-
"temperature": null,
111-
"max_tokens": null,
112-
"timeout": null,
113-
"reasoning_effort": null,
114-
"stream": null
115-
},
116-
"paths": {
117-
"context_path": null,
118-
"skill_path": null
62+
"profile": null,
63+
"base_url": null, "api_key": null, "model": null,
64+
"temperature": null, "max_tokens": null, "timeout": null,
65+
"reasoning_effort": null, "stream": null
11966
},
67+
"paths": { "context_path": null, "skill_path": null },
12068
"mcp": {
12169
"servers": {
122-
"example": {
123-
"transport": "stdio",
124-
"command": "npx",
125-
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
126-
"env": [],
127-
"parallel": false,
128-
"timeout": null,
129-
"enabled": false
130-
}
70+
"example": { "transport": "stdio", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"], "env": [], "parallel": false, "timeout": null, "enabled": false }
13171
}
13272
}
13373
}
13474
```
13575

136-
- `reasoning_effort` is passed to the API as-is (omitted when unset) — whatever your provider accepts ("low"/"medium"/"high").
137-
- Other optional keys: `backend`, `temperature`, `max_tokens`, `timeout`, `stream` (`true` by default; `run --no-stream` overrides on the command line).
138-
- `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.
139-
- **Named model profiles** (`models` section) enable runtime switching via `/model`: each profile is a partial LLM settings dict; unset keys inherit the main `llm`. Use `/model` in the TUI to switch between providers/models without restarting.
140-
- `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`).
141-
- `mcp.servers` configures MCP servers (requires the `[mcp]` extra). Each server is `{transport, command, args, env, url, headers, parallel, timeout, enabled}``stdio` needs `command`/`args` (optionally `env` naming environment variables to pass through, e.g. `["GITHUB_TOKEN"]`); `streamable-http`/`sse` need `url` (optionally `headers`). Its tools appear as `mcp__<server>__<tool>`.
142-
- 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`).
143-
- Use a custom config with `--config PATH` (or `PYTHON_AGENT_HARNESS_CONFIG`).
144-
145-
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.
76+
- Optional `llm` keys: `backend`, `temperature`, `max_tokens`, `timeout`, `reasoning_effort` (passed to the API as-is when set), `stream` (`run --no-stream` overrides).
77+
- **`models`** — named LLM profiles for runtime switching via `/model` (in the TUI: no arg lists, name or number switches). Each profile is a partial settings dict; unset keys inherit the main `llm`.
78+
- **`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.
79+
- **`paths.context_path` / `paths.skill_path`** — override context/skill discovery (defaults: `<project>/contexts` or `~/.emacs.d/contexts`; skills analog).
80+
- **`mcp.servers`** — requires the `[mcp]` extra; each server is `{transport, command, args, env, url, headers, parallel, timeout, enabled}`.
81+
- **Precedence**: code defaults < config file < `OPENAI_*` env vars. Sub-agent settings honor `OPENAI_SUBAGENT_*` (`_BASE_URL`, `_API_KEY`, `_MODEL`, `_BACKEND`).
82+
- Custom config: `--config PATH` or `PYTHON_AGENT_HARNESS_CONFIG`.
83+
- 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.
14684

14785
## Usage
14886

@@ -160,12 +98,12 @@ python-agent-harness run [project-dir] # interactive TUI agent
16098
| `/summary` | append a conversation summary |
16199
| `/save` | save the session |
162100
| `/sessions` | list saved sessions |
163-
| `/restore [path\|title\|--latest]` | restore a session (title substring match) |
101+
| `/restore [path\|title\|--latest\|latest]` | restore a session (title substring match) |
164102
| `/clear` | start a fresh conversation |
165-
| `/model [name]` | switch LLM model profile (no arg: list available; with arg: switch to that profile) |
103+
| `/model [name]` | switch LLM model profile (no arg: list available; with arg: switch) |
166104
| `/exit` | quit |
167105

168-
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).
106+
Custom commands from `prompts/commands/*.md` are registered as slash commands too (TUI-only).
169107

170108
## Project layout
171109

@@ -190,7 +128,7 @@ python_agent_harness/
190128

191129
## Development
192130

193-
Python ≥ 3.11 required (CI runs 3.11 / 3.12 / 3.13).
131+
Python ≥ 3.11 (CI runs 3.11 / 3.12 / 3.13).
194132

195133
```sh
196134
make test # unit tests (unittest discover)
@@ -201,17 +139,6 @@ venv/bin/python -m build # sdist + wheel
201139
venv/bin/pip-audit # dependency audit
202140
```
203141

204-
### Verification checklist (inherited semantics)
205-
206-
- [x] Nudge supervision with fail-closed dead-session budget
207-
- [x] Tool-result sanitization (None → error placeholder)
208-
- [x] Compaction: frame, resume last request
209-
- [x] Plan mode: read-only + plan-file writes only
210-
- [x] Bash: Ctrl-C process-group kill
211-
- [x] Session metadata round-trip and title sanitization
212-
- [x] One-shot LLM title generation after the agent run finishes
213-
- [x] Ctrl-C cancel: stale workers can't clobber the next run's history
214-
215142
## License
216143

217144
MIT

0 commit comments

Comments
 (0)