pdo-agent 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pdo_agent-2.0.0/LICENSE +21 -0
- pdo_agent-2.0.0/PKG-INFO +456 -0
- pdo_agent-2.0.0/README.md +426 -0
- pdo_agent-2.0.0/pyproject.toml +69 -0
- pdo_agent-2.0.0/setup.cfg +4 -0
- pdo_agent-2.0.0/src/pdo/__init__.py +21 -0
- pdo_agent-2.0.0/src/pdo/agent/__init__.py +6 -0
- pdo_agent-2.0.0/src/pdo/agent/core.py +275 -0
- pdo_agent-2.0.0/src/pdo/agent/delegate.py +56 -0
- pdo_agent-2.0.0/src/pdo/agent/executor.py +87 -0
- pdo_agent-2.0.0/src/pdo/agent/memory.py +191 -0
- pdo_agent-2.0.0/src/pdo/agent/messages.py +87 -0
- pdo_agent-2.0.0/src/pdo/agent/planner.py +38 -0
- pdo_agent-2.0.0/src/pdo/agent/reviewer.py +25 -0
- pdo_agent-2.0.0/src/pdo/agent/router.py +37 -0
- pdo_agent-2.0.0/src/pdo/api.py +65 -0
- pdo_agent-2.0.0/src/pdo/banner.py +53 -0
- pdo_agent-2.0.0/src/pdo/config.py +151 -0
- pdo_agent-2.0.0/src/pdo/llm.py +211 -0
- pdo_agent-2.0.0/src/pdo/logging_setup.py +34 -0
- pdo_agent-2.0.0/src/pdo/main.py +961 -0
- pdo_agent-2.0.0/src/pdo/mcp.py +264 -0
- pdo_agent-2.0.0/src/pdo/prompts/system.md +46 -0
- pdo_agent-2.0.0/src/pdo/providers.py +86 -0
- pdo_agent-2.0.0/src/pdo/rag.py +191 -0
- pdo_agent-2.0.0/src/pdo/serve.py +124 -0
- pdo_agent-2.0.0/src/pdo/skills.py +59 -0
- pdo_agent-2.0.0/src/pdo/theme.py +47 -0
- pdo_agent-2.0.0/src/pdo/tools/__init__.py +6 -0
- pdo_agent-2.0.0/src/pdo/tools/base.py +89 -0
- pdo_agent-2.0.0/src/pdo/tools/code.py +48 -0
- pdo_agent-2.0.0/src/pdo/tools/data.py +57 -0
- pdo_agent-2.0.0/src/pdo/tools/edit.py +55 -0
- pdo_agent-2.0.0/src/pdo/tools/filesystem.py +175 -0
- pdo_agent-2.0.0/src/pdo/tools/git.py +44 -0
- pdo_agent-2.0.0/src/pdo/tools/memory.py +70 -0
- pdo_agent-2.0.0/src/pdo/tools/rag.py +60 -0
- pdo_agent-2.0.0/src/pdo/tools/registry.py +203 -0
- pdo_agent-2.0.0/src/pdo/tools/search.py +83 -0
- pdo_agent-2.0.0/src/pdo/tools/shell.py +125 -0
- pdo_agent-2.0.0/src/pdo/tools/web.py +163 -0
- pdo_agent-2.0.0/src/pdo_agent.egg-info/PKG-INFO +456 -0
- pdo_agent-2.0.0/src/pdo_agent.egg-info/SOURCES.txt +63 -0
- pdo_agent-2.0.0/src/pdo_agent.egg-info/dependency_links.txt +1 -0
- pdo_agent-2.0.0/src/pdo_agent.egg-info/entry_points.txt +2 -0
- pdo_agent-2.0.0/src/pdo_agent.egg-info/requires.txt +9 -0
- pdo_agent-2.0.0/src/pdo_agent.egg-info/top_level.txt +1 -0
- pdo_agent-2.0.0/tests/test_agent.py +73 -0
- pdo_agent-2.0.0/tests/test_api.py +53 -0
- pdo_agent-2.0.0/tests/test_attachments.py +67 -0
- pdo_agent-2.0.0/tests/test_cli.py +46 -0
- pdo_agent-2.0.0/tests/test_config.py +50 -0
- pdo_agent-2.0.0/tests/test_delegate.py +115 -0
- pdo_agent-2.0.0/tests/test_extra_tools.py +81 -0
- pdo_agent-2.0.0/tests/test_filesystem.py +54 -0
- pdo_agent-2.0.0/tests/test_mcp.py +94 -0
- pdo_agent-2.0.0/tests/test_permissions.py +48 -0
- pdo_agent-2.0.0/tests/test_plugins.py +54 -0
- pdo_agent-2.0.0/tests/test_rag.py +83 -0
- pdo_agent-2.0.0/tests/test_registry.py +60 -0
- pdo_agent-2.0.0/tests/test_serve.py +97 -0
- pdo_agent-2.0.0/tests/test_sessions.py +45 -0
- pdo_agent-2.0.0/tests/test_shell.py +54 -0
- pdo_agent-2.0.0/tests/test_skills.py +34 -0
- pdo_agent-2.0.0/tests/test_summarize.py +36 -0
pdo_agent-2.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PDO Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pdo_agent-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pdo-agent
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: PDO (Python Do) — a terminal-first AI agent that reasons, plans, and safely executes real tasks.
|
|
5
|
+
Author: PDO Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/uaedoom/pdo
|
|
8
|
+
Project-URL: Repository, https://github.com/uaedoom/pdo
|
|
9
|
+
Project-URL: Issues, https://github.com/uaedoom/pdo/issues
|
|
10
|
+
Keywords: ai,agent,terminal,cli,llm,openai,automation
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
17
|
+
Classifier: Topic :: Utilities
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: openai<2,>=1.30
|
|
22
|
+
Requires-Dist: python-dotenv<2,>=1.0
|
|
23
|
+
Requires-Dist: rich<14,>=13.7
|
|
24
|
+
Requires-Dist: pydantic<3,>=2.6
|
|
25
|
+
Requires-Dist: prompt_toolkit<4,>=3.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest<9,>=8.0; extra == "dev"
|
|
28
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
<h1 align="center">PDO — Python Do</h1>
|
|
32
|
+
|
|
33
|
+
<div align="center">
|
|
34
|
+
<pre>
|
|
35
|
+
████████ ██████ ████████
|
|
36
|
+
██ ██ ██ ██ ██ ██
|
|
37
|
+
████████ ██ ██ ██ ██
|
|
38
|
+
██ ██ ██ ██ ██
|
|
39
|
+
██ ██████ ████████
|
|
40
|
+
</pre>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<p align="center"><b>Think. Plan. Do.</b><br/><sub>The same pixel-art logo greets you on every launch.</sub></p>
|
|
44
|
+
|
|
45
|
+
<p align="center">
|
|
46
|
+
<img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-green.svg">
|
|
47
|
+
<img alt="Python 3.12+" src="https://img.shields.io/badge/Python-3.12%2B-blue.svg">
|
|
48
|
+
<img alt="Open Source" src="https://img.shields.io/badge/Open%20Source-%E2%9D%A4-red.svg">
|
|
49
|
+
</p>
|
|
50
|
+
|
|
51
|
+
> **PDO is free and open source** (MIT licensed). Contributions are welcome —
|
|
52
|
+
> see [CONTRIBUTING.md](CONTRIBUTING.md). Star the repo if you find it useful! ⭐
|
|
53
|
+
|
|
54
|
+
PDO is a terminal-first AI agent that completes real tasks — it doesn't just
|
|
55
|
+
answer questions. Give it a goal and it reasons about it, plans the steps,
|
|
56
|
+
decides whether tools are needed, executes them **safely**, reviews the result,
|
|
57
|
+
and replies clearly. When a plain answer is enough, it just answers; it never
|
|
58
|
+
reaches for a tool it doesn't need.
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
you ▸ list all markdown files in this repo and summarise the README
|
|
62
|
+
🔧 run_shell(command='find . -name "*.md"')
|
|
63
|
+
🔧 read_file(path='README.md')
|
|
64
|
+
PDO Here are the Markdown files… and a three-line summary of the README…
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Features
|
|
70
|
+
|
|
71
|
+
- **ReAct-style agent loop** built on the LLM's **native function/tool calling** —
|
|
72
|
+
the model picks tools and arguments; PDO executes them and feeds results back
|
|
73
|
+
until the task is done. Tool calls are never parsed out of free text.
|
|
74
|
+
- **Many providers** — OpenAI, Anthropic, OpenRouter, local **Ollama**, or any
|
|
75
|
+
OpenAI-compatible endpoint. Switch provider and model at runtime with `/models`
|
|
76
|
+
(live model listing). The core depends only on an `LLMClient` interface.
|
|
77
|
+
- **18 built-in tools** — filesystem (read / write / append / **edit** / list /
|
|
78
|
+
mkdir), shell (dangerous-command guard), code search (**glob** / **grep**),
|
|
79
|
+
**git**, **web search & fetch**, **HTTP**, **Python exec**, **SQLite**, and
|
|
80
|
+
long-term memory (save / search / delete).
|
|
81
|
+
- **Extensible** three ways without touching the core:
|
|
82
|
+
- **Plugins** — drop a `Tool` subclass in `<PDO_HOME>/plugins/` (or ship one via
|
|
83
|
+
the `pdo.plugins` entry-point group).
|
|
84
|
+
- **Skills** — Markdown prompt recipes become slash commands (`/review`, …).
|
|
85
|
+
- **MCP** — connect any Model Context Protocol server; its tools appear as
|
|
86
|
+
`mcp__<server>__<tool>`.
|
|
87
|
+
- **Conversation management** — named **sessions** (`/new`, `/resume`), automatic
|
|
88
|
+
**summarisation** of long history, `@file` references (text **and images** for
|
|
89
|
+
vision models), and `/export`.
|
|
90
|
+
- **Sub-agents** — a `delegate_task` tool spawns a fresh child agent for
|
|
91
|
+
self-contained subtasks, keeping the main context small on big jobs.
|
|
92
|
+
- **Codebase search** — `/index` builds a local BM25 index of your project; the
|
|
93
|
+
agent then uses `codebase_search` to find relevant code with `path:line` refs
|
|
94
|
+
(no embeddings API needed — works fully offline).
|
|
95
|
+
- **Safety & control** — typed confirmation for destructive commands, working-dir
|
|
96
|
+
write sandbox, per-tool **permission policies**, and a structured **audit log**.
|
|
97
|
+
- **Polished terminal UX** — pixel-art splash, a bordered input box with slash
|
|
98
|
+
autocomplete, Markdown rendering, a thinking spinner, color **themes**, and a
|
|
99
|
+
live token-usage footer.
|
|
100
|
+
- **Scriptable** — one-shot mode (`pdo "prompt"`), `--json` output, and a Docker
|
|
101
|
+
image.
|
|
102
|
+
- **Tested & CI-ready** — a `pytest` suite that mocks the LLM (no API key needed)
|
|
103
|
+
and a GitHub Actions workflow.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Installation
|
|
108
|
+
|
|
109
|
+
> [!IMPORTANT]
|
|
110
|
+
> **PDO requires Python 3.12+.** Create the virtual environment with a 3.12
|
|
111
|
+
> interpreter explicitly — don't rely on the system `python3` (macOS ships 3.9,
|
|
112
|
+
> which will not work).
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# 1. Clone
|
|
116
|
+
git clone https://github.com/uaedoom/pdo.git
|
|
117
|
+
cd pdo
|
|
118
|
+
|
|
119
|
+
# 2. Create a virtual environment WITH Python 3.12+
|
|
120
|
+
python3.12 -m venv .venv # macOS (Homebrew): brew install python@3.12
|
|
121
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
122
|
+
|
|
123
|
+
# 3. Upgrade pip, then install (editable, with dev extras)
|
|
124
|
+
python -m pip install --upgrade pip
|
|
125
|
+
pip install -e ".[dev]"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This installs the `pdo` console command. Verify with `python --version`
|
|
129
|
+
(should be 3.12.x) and `pdo --version`.
|
|
130
|
+
|
|
131
|
+
### Notes for users / troubleshooting
|
|
132
|
+
|
|
133
|
+
- **`ERROR: ... Directory cannot be installed in editable mode` / "requires a
|
|
134
|
+
setuptools-based build"** — your virtual environment is on an old Python (and
|
|
135
|
+
old pip). Recreate it with Python 3.12 and upgrade pip:
|
|
136
|
+
```bash
|
|
137
|
+
deactivate; rm -rf .venv
|
|
138
|
+
python3.12 -m venv .venv && source .venv/bin/activate
|
|
139
|
+
python -m pip install --upgrade pip
|
|
140
|
+
pip install -e ".[dev]"
|
|
141
|
+
```
|
|
142
|
+
- **No `python3.12`?** Install it first: macOS `brew install python@3.12`,
|
|
143
|
+
Ubuntu `sudo apt install python3.12 python3.12-venv`.
|
|
144
|
+
- **Not yet on PyPI** — install by cloning as above (`pip install pdo` isn't
|
|
145
|
+
available yet).
|
|
146
|
+
- **Tested on macOS and Linux.** Windows should work but is less tested.
|
|
147
|
+
- Runtime data (memory, sessions, logs) lives in `~/.pdo` if you set
|
|
148
|
+
`PDO_HOME=~/.pdo`; otherwise it defaults to the package directory.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Quick Start
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Set your API key (or copy .env.example to .env and fill it in)
|
|
156
|
+
export OPENAI_API_KEY=sk-...
|
|
157
|
+
|
|
158
|
+
# Optionally choose a model (gpt-4.1-mini is the default)
|
|
159
|
+
export OPENAI_MODEL=gpt-4.1-mini
|
|
160
|
+
|
|
161
|
+
# Run it interactively
|
|
162
|
+
pdo
|
|
163
|
+
|
|
164
|
+
# …or one-shot (great for scripts / pipes)
|
|
165
|
+
pdo "list all markdown files and summarise the README"
|
|
166
|
+
pdo --json "what is 2+2" # machine-readable output
|
|
167
|
+
pdo --version
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Interactive mode gives you a prompt; type a goal, or a slash command (`/help`).
|
|
171
|
+
Replies render as Markdown, with a thinking spinner and a Codex-style activity
|
|
172
|
+
log. Switch colors live with `/theme green` (or set `PDO_THEME`).
|
|
173
|
+
|
|
174
|
+
### Configuration
|
|
175
|
+
|
|
176
|
+
All configuration is read from the environment (a `.env` file is auto-loaded):
|
|
177
|
+
|
|
178
|
+
| Variable | Default | Description |
|
|
179
|
+
| ----------------- | ---------------- | --------------------------------------------- |
|
|
180
|
+
| `OPENAI_API_KEY` | *(required)* | Your API key (OpenAI **or** OpenRouter, etc.). |
|
|
181
|
+
| `OPENAI_MODEL` | `gpt-4.1-mini` | Model to use. |
|
|
182
|
+
| `OPENAI_BASE_URL` | *(OpenAI)* | API endpoint. Set to use an OpenAI-compatible provider. |
|
|
183
|
+
| `TEMPERATURE` | `0.2` | Sampling temperature (0–2). |
|
|
184
|
+
| `PDO_HOME` | package dir | Where memory and logs are stored (e.g. `~/.pdo`). |
|
|
185
|
+
|
|
186
|
+
PDO fails fast with a friendly message if `OPENAI_API_KEY` is missing.
|
|
187
|
+
|
|
188
|
+
### Using OpenRouter (or other OpenAI-compatible APIs)
|
|
189
|
+
|
|
190
|
+
PDO talks to any OpenAI-compatible endpoint — just point `OPENAI_BASE_URL` at it
|
|
191
|
+
and use that provider's key and model names. For [OpenRouter](https://openrouter.ai):
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
export OPENAI_API_KEY=sk-or-... # your OpenRouter key
|
|
195
|
+
export OPENAI_BASE_URL=https://openrouter.ai/api/v1
|
|
196
|
+
export OPENAI_MODEL=openai/gpt-4.1-mini # any OpenRouter model id
|
|
197
|
+
pdo
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The same pattern works for local servers (e.g. Ollama/LM Studio at
|
|
201
|
+
`http://localhost:11434/v1`). The model must support **tool/function calling**
|
|
202
|
+
for PDO's agent loop to use tools.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Example Usage
|
|
207
|
+
|
|
208
|
+
```text
|
|
209
|
+
you ▸ build a minimal Flask API in ./hello-api
|
|
210
|
+
you ▸ explain this repository
|
|
211
|
+
you ▸ fix this Python error: <paste traceback>
|
|
212
|
+
you ▸ list all markdown files
|
|
213
|
+
you ▸ create a README for this project
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Terminal commands
|
|
217
|
+
|
|
218
|
+
| Command | What it does |
|
|
219
|
+
| ----------- | ------------------------------------- |
|
|
220
|
+
| `/help` | Show available commands |
|
|
221
|
+
| `/models` | Switch provider & model (OpenAI / Anthropic / OpenRouter / Ollama) |
|
|
222
|
+
| `/tools` | List registered tools |
|
|
223
|
+
| `/mcp` | Show connected MCP servers and their tools |
|
|
224
|
+
| `/theme` | Change the color theme (e.g. `/theme green`) |
|
|
225
|
+
| `/export` | Save the conversation to a Markdown file |
|
|
226
|
+
| `/sessions` | List saved conversation sessions |
|
|
227
|
+
| `/new` | Start a new session (e.g. `/new feature-x`) |
|
|
228
|
+
| `/resume` | Switch to another session (e.g. `/resume default`) |
|
|
229
|
+
| `/memory` | Show saved facts and preferences |
|
|
230
|
+
| `/history` | Show recent conversation history |
|
|
231
|
+
| `/clear` | Clear the current session's history |
|
|
232
|
+
| `/version` | Show the PDO version |
|
|
233
|
+
| `/exit` | Quit |
|
|
234
|
+
|
|
235
|
+
### Switching models at runtime
|
|
236
|
+
|
|
237
|
+
Type `/models` to pick a provider (OpenAI, Anthropic, or OpenRouter) and a model
|
|
238
|
+
interactively — the change applies immediately for the rest of the session. If a
|
|
239
|
+
key for the chosen provider isn't in your environment, PDO prompts for one and
|
|
240
|
+
keeps it in memory for the session only (never written to disk). Set
|
|
241
|
+
`ANTHROPIC_API_KEY` / `OPENROUTER_API_KEY` in your `.env` to skip the prompt.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Project Structure
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
pdo/
|
|
249
|
+
├─ pyproject.toml # Packaging + console script (pdo = pdo.main:main)
|
|
250
|
+
├─ requirements.txt # Convenience mirror of runtime deps
|
|
251
|
+
├─ .env.example # Sample configuration
|
|
252
|
+
├─ .github/workflows/ci.yml
|
|
253
|
+
├─ src/pdo/
|
|
254
|
+
│ ├─ main.py # Terminal entry point + REPL + slash commands
|
|
255
|
+
│ ├─ config.py # Env-based config, validated with pydantic
|
|
256
|
+
│ ├─ llm.py # LLMClient interface + OpenAI implementation
|
|
257
|
+
│ ├─ logging_setup.py # Rotating file logging for the `pdo` namespace
|
|
258
|
+
│ ├─ agent/
|
|
259
|
+
│ │ ├─ core.py # Coordinates components; runs the ReAct loop
|
|
260
|
+
│ │ ├─ planner.py # Breaks a goal into steps (thin, advisory)
|
|
261
|
+
│ │ ├─ router.py # Plain chat vs. tool use (thin; model decides)
|
|
262
|
+
│ │ ├─ executor.py # Runs approved tool calls, safely
|
|
263
|
+
│ │ ├─ reviewer.py # Sanity-checks the final answer
|
|
264
|
+
│ │ ├─ memory.py # Local JSON memory store
|
|
265
|
+
│ │ └─ messages.py # Message/ToolCall dataclasses
|
|
266
|
+
│ ├─ tools/
|
|
267
|
+
│ │ ├─ base.py # Tool base class + confirmation helper
|
|
268
|
+
│ │ ├─ registry.py # The single tool registry + auto-registration
|
|
269
|
+
│ │ ├─ filesystem.py # read / write / append / list / mkdir
|
|
270
|
+
│ │ ├─ shell.py # run command + dangerous-command detector
|
|
271
|
+
│ │ └─ memory.py # save / search / delete memory tools
|
|
272
|
+
│ ├─ prompts/system.md # The system prompt
|
|
273
|
+
│ ├─ data/ # Runtime JSON state (memory.json, history.json)
|
|
274
|
+
│ └─ logs/ # Rotating logs (pdo.log)
|
|
275
|
+
├─ tests/ # pytest suite (LLM is mocked)
|
|
276
|
+
└─ docs/ # Architecture notes
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
> **Where is my data?** By default PDO stores `memory.json`, `history.json` and
|
|
280
|
+
> logs inside the installed package directory so a fresh clone works immediately.
|
|
281
|
+
> Set `PDO_HOME=~/.pdo` to keep that state in your home directory instead.
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Adding New Tools
|
|
286
|
+
|
|
287
|
+
A tool is a small class. Subclass `Tool`, declare a JSON parameter schema, and
|
|
288
|
+
decorate it with `@register_tool` — that's it. The agent picks it up
|
|
289
|
+
automatically; you never touch the core.
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
# src/pdo/tools/clock.py
|
|
293
|
+
from datetime import datetime
|
|
294
|
+
from typing import Any
|
|
295
|
+
|
|
296
|
+
from .base import Tool
|
|
297
|
+
from .registry import register_tool
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@register_tool
|
|
301
|
+
class CurrentTimeTool(Tool):
|
|
302
|
+
name = "current_time"
|
|
303
|
+
description = "Return the current local date and time."
|
|
304
|
+
parameters = {"type": "object", "properties": {}}
|
|
305
|
+
|
|
306
|
+
def run(self, **_: Any) -> str:
|
|
307
|
+
return datetime.now().isoformat(timespec="seconds")
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
For a built-in tool, add the module to the lazy import in
|
|
311
|
+
`tools/registry.py:get_registry`. For tools that perform sensitive actions,
|
|
312
|
+
accept an injectable `confirm` callback (see `tools/filesystem.py`) so they can
|
|
313
|
+
be tested deterministically and prompt the user when needed.
|
|
314
|
+
|
|
315
|
+
### Plugins (no fork required)
|
|
316
|
+
|
|
317
|
+
You don't have to edit PDO to add a tool. PDO **auto-discovers plugins** on
|
|
318
|
+
startup from two places:
|
|
319
|
+
|
|
320
|
+
1. **A plugins directory** — drop a `.py` file defining a `Tool` subclass into
|
|
321
|
+
your plugins folder (run `/tools` to see its path; default
|
|
322
|
+
`<PDO_HOME>/plugins`). The `@register_tool` decorator is optional there — PDO
|
|
323
|
+
finds `Tool` subclasses automatically. A ready-made example lives in
|
|
324
|
+
[`examples/plugins/current_time_tool.py`](examples/plugins/current_time_tool.py):
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
mkdir -p ~/.pdo/plugins # if you run with PDO_HOME=~/.pdo
|
|
328
|
+
cp examples/plugins/current_time_tool.py ~/.pdo/plugins/
|
|
329
|
+
pdo # the `current_time` tool is now available
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
2. **Installed packages** — a third-party package can advertise tools via the
|
|
333
|
+
`pdo.plugins` entry-point group. Each entry point may resolve to a `Tool`
|
|
334
|
+
subclass or a `register(registry)` callable:
|
|
335
|
+
|
|
336
|
+
```toml
|
|
337
|
+
# in the plugin package's pyproject.toml
|
|
338
|
+
[project.entry-points."pdo.plugins"]
|
|
339
|
+
my_tool = "my_package.tools:MyTool"
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
A broken plugin is logged and skipped — it never crashes PDO.
|
|
343
|
+
|
|
344
|
+
### Skills (reusable prompt commands)
|
|
345
|
+
|
|
346
|
+
Drop a Markdown file in your skills directory (`<PDO_HOME>/skills/`) and it
|
|
347
|
+
becomes a slash command named after the file. An optional first line
|
|
348
|
+
`description: …` (or `# Title`) sets the menu text, and `{{args}}` interpolates
|
|
349
|
+
whatever you type after the command. Example
|
|
350
|
+
[`examples/skills/review.md`](examples/skills/review.md) becomes `/review`:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
mkdir -p ~/.pdo/skills
|
|
354
|
+
cp examples/skills/review.md ~/.pdo/skills/
|
|
355
|
+
pdo # now type: /review the auth module
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Referencing files with `@`
|
|
359
|
+
|
|
360
|
+
In any message, mention a file with `@path` and PDO inlines its contents for the
|
|
361
|
+
model — e.g. `explain @src/pdo/main.py` or `fix the bug in @app.py`.
|
|
362
|
+
|
|
363
|
+
**Images too:** `@screenshot.png` (png/jpg/gif/webp) attaches the image itself,
|
|
364
|
+
so vision-capable models can see it — e.g. `what's wrong in this UI? @shot.png`.
|
|
365
|
+
|
|
366
|
+
### Multi-line input
|
|
367
|
+
|
|
368
|
+
Press **Enter** to send. Press **Option/Alt+Enter** (⌥⏎) to insert a newline and
|
|
369
|
+
compose a multi-line message before sending.
|
|
370
|
+
|
|
371
|
+
### MCP servers (Model Context Protocol)
|
|
372
|
+
|
|
373
|
+
PDO is an MCP client: connect any MCP server and its tools become available to
|
|
374
|
+
the agent automatically (named `mcp__<server>__<tool>`). Declare servers in
|
|
375
|
+
`<PDO_HOME>/mcp.json` using the standard format (see
|
|
376
|
+
[`examples/mcp.json`](examples/mcp.json)):
|
|
377
|
+
|
|
378
|
+
```json
|
|
379
|
+
{
|
|
380
|
+
"mcpServers": {
|
|
381
|
+
"filesystem": {
|
|
382
|
+
"command": "npx",
|
|
383
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Servers start on launch (over the stdio transport); run `/mcp` to see what's
|
|
390
|
+
connected. A server that fails to start is reported and skipped — it never
|
|
391
|
+
crashes PDO. No extra Python dependencies are required.
|
|
392
|
+
|
|
393
|
+
### PDO *as* an MCP server / SDK
|
|
394
|
+
|
|
395
|
+
It works both ways — `pdo --serve` exposes the whole agent as an **MCP server**
|
|
396
|
+
over stdio, so Claude Desktop, Claude Code, or any MCP client can call its
|
|
397
|
+
`run_task` tool and get back the agent's final answer (with all of PDO's tools,
|
|
398
|
+
sub-agents, and codebase search behind it). For example, in a client's
|
|
399
|
+
`mcp.json`:
|
|
400
|
+
|
|
401
|
+
```json
|
|
402
|
+
{ "mcpServers": { "pdo": { "command": "pdo", "args": ["--serve"] } } }
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
In serve mode nothing is printed to stdout and interactive confirmations are
|
|
406
|
+
auto-denied (dangerous commands are refused rather than prompted).
|
|
407
|
+
|
|
408
|
+
And from Python scripts, embed the agent directly:
|
|
409
|
+
|
|
410
|
+
```python
|
|
411
|
+
from pdo import run_agent
|
|
412
|
+
|
|
413
|
+
answer = run_agent("list the markdown files here and summarise the README")
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
`run_agent` accepts overrides (`model=`, `base_url=`, `api_key=`,
|
|
417
|
+
`temperature=`, or a custom `llm=` client) and uses an ephemeral memory so it
|
|
418
|
+
never touches your interactive sessions.
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## Roadmap
|
|
423
|
+
|
|
424
|
+
**v1 — done**
|
|
425
|
+
- Native tool-calling agent loop; multi-provider (OpenAI / Anthropic / OpenRouter
|
|
426
|
+
/ Ollama); 18 built-in tools; plugins, skills, and MCP client; named sessions +
|
|
427
|
+
auto-summary; permission policies + audit log; themed TUI with one-shot/JSON
|
|
428
|
+
modes; tests + CI; Docker.
|
|
429
|
+
|
|
430
|
+
**v2 (this release) — done**
|
|
431
|
+
- Multi-line input and image/vision attachments (`@image.png`).
|
|
432
|
+
- Sub-agents (`delegate_task`) and codebase retrieval (`/index` +
|
|
433
|
+
`codebase_search`, BM25 — fully offline).
|
|
434
|
+
- `pdo --serve` (PDO as an MCP server) and the `run_agent` Python API.
|
|
435
|
+
|
|
436
|
+
**Next**
|
|
437
|
+
- Embedding-based retrieval as an optional upgrade to the BM25 index.
|
|
438
|
+
- Streamed responses inside serve mode; richer sub-agent orchestration.
|
|
439
|
+
- PyPI publication and a Homebrew formula.
|
|
440
|
+
|
|
441
|
+
Each of these arrives as a new `Tool` (or `LLMClient`) — by design, none require
|
|
442
|
+
changes to the core.
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
## Contributing
|
|
447
|
+
|
|
448
|
+
Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for setup,
|
|
449
|
+
coding standards, and how to add tools. Please run `ruff check .` and `pytest`
|
|
450
|
+
before opening a pull request.
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## License
|
|
455
|
+
|
|
456
|
+
[MIT](LICENSE) © PDO Contributors
|