|
1 | 1 | # py-agent-ctrl |
2 | 2 |
|
3 | | -Programmatic wrapper for the **Claude Code CLI** — Python port of [cognesy/agent-ctrl](https://github.com/cognesy/agent-ctrl) (PHP). |
| 3 | +Unified Python bridge for CLI coding agents. |
4 | 4 |
|
5 | | -Zero dependencies beyond the Python standard library. |
| 5 | +This repo exposes: |
6 | 6 |
|
7 | | -## Install |
| 7 | +- a Python API under `py_agent_ctrl` |
| 8 | +- a unified `ctrlagent` CLI |
| 9 | +- direct CLI bridges for `claude`, `codex`, `opencode`, `pi`, and `gemini` |
| 10 | + |
| 11 | +The codebase follows the clean layout described in [docs/dev/architecture.md](docs/dev/architecture.md): |
| 12 | + |
| 13 | +- `apps/` for runnable shells |
| 14 | +- `libs/` for importable code |
| 15 | +- `resources/` for passive assets |
| 16 | +- `docs/` for documentation |
| 17 | +- `tests/` for unit, integration, feature, and regression coverage |
| 18 | + |
| 19 | +## Development |
| 20 | + |
| 21 | +Use `uv` only: |
8 | 22 |
|
9 | 23 | ```bash |
10 | | -pip install git+https://github.com/cognesy/py-agent-ctrl.git@main |
| 24 | +uv sync --extra dev |
| 25 | +uv run pytest tests/unit tests/integration tests/feature tests/regression |
| 26 | +uv run ctrlagent agents list |
11 | 27 | ``` |
12 | 28 |
|
13 | | -## Usage |
| 29 | +## Python API |
14 | 30 |
|
15 | 31 | ```python |
16 | | -from agent_ctrl import ClaudeCodeClient |
| 32 | +from py_agent_ctrl import AgentCtrl |
| 33 | + |
| 34 | +response = ( |
| 35 | + AgentCtrl.claude_code() |
| 36 | + .with_model("claude-sonnet-4-5") |
| 37 | + .with_permission_mode("bypassPermissions") |
| 38 | + .execute("Summarize this repository.") |
| 39 | +) |
17 | 40 |
|
18 | | -# Simple sync |
19 | | -response = ClaudeCodeClient().execute("Summarise this repo.") |
20 | 41 | print(response.text) |
21 | 42 | print(response.session_id) |
| 43 | +``` |
22 | 44 |
|
23 | | -# With options |
24 | | -response = ( |
25 | | - ClaudeCodeClient() |
26 | | - .with_system_prompt("You are a code analysis assistant.") |
27 | | - .with_max_turns(5) |
28 | | - .with_cwd("/path/to/project") |
29 | | - .execute("List all API endpoints.") |
30 | | -) |
| 45 | +Other bridges use the same top-level facade: |
31 | 46 |
|
32 | | -# Session continuity |
33 | | -r1 = ClaudeCodeClient().execute("Start a multi-turn analysis.") |
34 | | -r2 = ClaudeCodeClient().resume(r1.session_id).execute("Now summarise what you found.") |
| 47 | +```python |
| 48 | +from py_agent_ctrl import AgentCtrl |
35 | 49 |
|
36 | | -# Async streaming with callbacks |
37 | | -import asyncio |
| 50 | +AgentCtrl.codex().with_sandbox("workspace-write").execute("Review the tests.") |
| 51 | +AgentCtrl.opencode().with_agent("coder").execute("Refactor the payment flow.") |
| 52 | +AgentCtrl.pi().with_thinking("high").execute("Create an implementation plan.") |
| 53 | +AgentCtrl.gemini().plan_mode().execute("Inspect the architecture.") |
| 54 | +``` |
38 | 55 |
|
39 | | -async def main(): |
40 | | - async for event in ClaudeCodeClient().stream("Explain this codebase."): |
41 | | - from agent_ctrl import AssistantEvent |
42 | | - if isinstance(event, AssistantEvent): |
43 | | - print(event.text, end="", flush=True) |
| 56 | +## CLI |
44 | 57 |
|
45 | | -asyncio.run(main()) |
| 58 | +```bash |
| 59 | +uv run ctrlagent agents list |
| 60 | +uv run ctrlagent agents capabilities --agent claude-code |
| 61 | +uv run ctrlagent execute --agent claude-code "Summarize this repository." |
| 62 | +uv run ctrlagent resume --agent codex --session thread_123 "Continue." |
| 63 | +uv run ctrlagent continue --agent gemini "Proceed." |
46 | 64 | ``` |
47 | 65 |
|
48 | | -## ClaudeCodeClient builder options |
49 | | - |
50 | | -| Method | Description | |
51 | | -|--------|-------------| |
52 | | -| `.with_model(model)` | Override the Claude model | |
53 | | -| `.with_system_prompt(text)` | Replace the default system prompt | |
54 | | -| `.append_system_prompt(text)` | Append to the default system prompt | |
55 | | -| `.with_max_turns(n)` | Limit agentic turns | |
56 | | -| `.with_permission_mode(mode)` | `bypassPermissions` (default) / `acceptEdits` / `default` | |
57 | | -| `.with_allowed_tools(*tools)` | Pre-approve specific tools, e.g. `"Read"`, `"Edit"` | |
58 | | -| `.resume(session_id)` | Resume a specific prior session | |
59 | | -| `.continue_session()` | Resume the most recent session | |
60 | | -| `.with_cwd(path)` | Working directory for the subprocess | |
61 | | -| `.with_timeout(seconds)` | Subprocess timeout (default 120s) | |
62 | | -| `.on_text(fn)` | Callback for each text chunk during async streaming | |
63 | | -| `.on_tool_use(fn)` | Callback for each tool use during async streaming | |
64 | | - |
65 | | -## ClaudeResponse |
66 | | - |
67 | | -| Field | Type | Description | |
68 | | -|-------|------|-------------| |
69 | | -| `text` | `str` | Concatenated assistant text | |
70 | | -| `session_id` | `str \| None` | Session ID for resuming | |
71 | | -| `success` | `bool` | `exit_code == 0 and not is_error` | |
72 | | -| `tool_calls` | `list[ToolUseContent]` | Tools invoked during the run | |
73 | | -| `cost_usd` | `float \| None` | Reported cost (if available) | |
74 | | -| `duration_ms` | `int \| None` | Reported duration (if available) | |
75 | | -| `events` | `list[StreamEvent]` | All raw typed events | |
76 | | - |
77 | | -## Requirements |
78 | | - |
79 | | -- Python 3.12+ |
80 | | -- `claude` CLI installed and on `PATH`: `npm install -g @anthropic-ai/claude-code` |
81 | | -- `ANTHROPIC_API_KEY` set in the environment |
| 66 | +## Supported Agents |
| 67 | + |
| 68 | +| Agent | Python Facade | CLI Name | |
| 69 | +|-------|---------------|----------| |
| 70 | +| Claude Code | `AgentCtrl.claude_code()` | `claude-code` | |
| 71 | +| Codex | `AgentCtrl.codex()` | `codex` | |
| 72 | +| OpenCode | `AgentCtrl.opencode()` | `opencode` | |
| 73 | +| Pi | `AgentCtrl.pi()` | `pi` | |
| 74 | +| Gemini | `AgentCtrl.gemini()` | `gemini` | |
| 75 | + |
| 76 | +## Migration |
| 77 | + |
| 78 | +This repo is a clean cut from the old flat Claude-only layout. |
| 79 | + |
| 80 | +- old root package: `agent_ctrl` |
| 81 | +- new root package: `py_agent_ctrl` |
| 82 | +- old client: `ClaudeCodeClient` |
| 83 | +- new entrypoint: `AgentCtrl` |
| 84 | + |
| 85 | +See: |
| 86 | + |
| 87 | +- [docs/user/quickstart.md](docs/user/quickstart.md) |
| 88 | +- [docs/user/cli.md](docs/user/cli.md) |
| 89 | +- [docs/user/migration.md](docs/user/migration.md) |
| 90 | +- [resources/skills/upgrade/SKILL.md](resources/skills/upgrade/SKILL.md) |
0 commit comments