pi-py-sdk 0.1.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.
Files changed (37) hide show
  1. pi_py_sdk-0.1.0/.github/workflows/ci.yml +58 -0
  2. pi_py_sdk-0.1.0/.github/workflows/publish.yml +26 -0
  3. pi_py_sdk-0.1.0/.gitignore +17 -0
  4. pi_py_sdk-0.1.0/CLAUDE.md +89 -0
  5. pi_py_sdk-0.1.0/LICENSE +21 -0
  6. pi_py_sdk-0.1.0/PKG-INFO +155 -0
  7. pi_py_sdk-0.1.0/README.md +129 -0
  8. pi_py_sdk-0.1.0/docs/python-sdk-plan.md +236 -0
  9. pi_py_sdk-0.1.0/examples/one_shot.py +32 -0
  10. pi_py_sdk-0.1.0/examples/sync_usage.py +31 -0
  11. pi_py_sdk-0.1.0/examples/with_approvals.py +47 -0
  12. pi_py_sdk-0.1.0/pyproject.toml +52 -0
  13. pi_py_sdk-0.1.0/src/pi_py_agent/__init__.py +8 -0
  14. pi_py_sdk-0.1.0/src/pi_py_agent/app.py +337 -0
  15. pi_py_sdk-0.1.0/src/pi_py_agent/cli.py +53 -0
  16. pi_py_sdk-0.1.0/src/pi_py_agent/py.typed +0 -0
  17. pi_py_sdk-0.1.0/src/pi_py_agent/render.py +137 -0
  18. pi_py_sdk-0.1.0/src/pi_py_sdk/__init__.py +104 -0
  19. pi_py_sdk-0.1.0/src/pi_py_sdk/_discovery.py +41 -0
  20. pi_py_sdk-0.1.0/src/pi_py_sdk/client.py +436 -0
  21. pi_py_sdk-0.1.0/src/pi_py_sdk/config.py +51 -0
  22. pi_py_sdk-0.1.0/src/pi_py_sdk/errors.py +37 -0
  23. pi_py_sdk-0.1.0/src/pi_py_sdk/jsonl.py +66 -0
  24. pi_py_sdk-0.1.0/src/pi_py_sdk/protocol.py +310 -0
  25. pi_py_sdk-0.1.0/src/pi_py_sdk/py.typed +0 -0
  26. pi_py_sdk-0.1.0/src/pi_py_sdk/sync.py +123 -0
  27. pi_py_sdk-0.1.0/src/pi_py_sdk/transport.py +132 -0
  28. pi_py_sdk-0.1.0/tests/test_client_fake.py +87 -0
  29. pi_py_sdk-0.1.0/tests/test_commands.py +99 -0
  30. pi_py_sdk-0.1.0/tests/test_integration.py +94 -0
  31. pi_py_sdk-0.1.0/tests/test_jsonl.py +81 -0
  32. pi_py_sdk-0.1.0/tests/test_messages.py +88 -0
  33. pi_py_sdk-0.1.0/tests/test_protocol.py +71 -0
  34. pi_py_sdk-0.1.0/tests/test_render.py +105 -0
  35. pi_py_sdk-0.1.0/tests/test_repl_helpers.py +49 -0
  36. pi_py_sdk-0.1.0/tests/test_sync.py +77 -0
  37. pi_py_sdk-0.1.0/tests/test_ui_and_events.py +150 -0
@@ -0,0 +1,58 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ name: unit (py${{ matrix.python-version }})
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+ - run: python -m pip install --upgrade pip
22
+ - run: pip install -e ".[dev]"
23
+ - name: Run unit tests
24
+ run: pytest -q
25
+
26
+ build:
27
+ name: build wheel
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - uses: actions/setup-python@v5
32
+ with:
33
+ python-version: "3.12"
34
+ - run: pip install build
35
+ - run: python -m build
36
+ - uses: actions/upload-artifact@v4
37
+ with:
38
+ name: dist
39
+ path: dist/*
40
+
41
+ integration:
42
+ name: integration (live pi, best-effort)
43
+ runs-on: ubuntu-latest
44
+ # Best-effort: spins up a real `pi` to smoke the bridge. Never blocks merges,
45
+ # since it depends on the upstream npm package and default-model availability.
46
+ continue-on-error: true
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+ - uses: actions/setup-python@v5
50
+ with:
51
+ python-version: "3.12"
52
+ - uses: actions/setup-node@v4
53
+ with:
54
+ node-version: "20"
55
+ - run: npm install -g @earendil-works/pi-coding-agent
56
+ - run: pip install -e ".[dev]"
57
+ - name: Run integration tests (non-LLM)
58
+ run: pytest -m integration -q
@@ -0,0 +1,26 @@
1
+ name: Publish
2
+
3
+ # Builds the distribution on every release and publishes it to PyPI.
4
+ #
5
+ # Setup required once: configure a PyPI Trusted Publisher for this repo/workflow
6
+ # (https://docs.pypi.org/trusted-publishers/) so no API token secret is needed.
7
+
8
+ on:
9
+ release:
10
+ types: [published]
11
+
12
+ jobs:
13
+ publish:
14
+ runs-on: ubuntu-latest
15
+ environment: pypi
16
+ permissions:
17
+ id-token: write # OIDC token for PyPI trusted publishing
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.12"
23
+ - run: pip install build
24
+ - run: python -m build
25
+ - name: Publish to PyPI
26
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,17 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ venv/
10
+ .env
11
+
12
+ # Tooling
13
+ .pytest_cache/
14
+ .mypy_cache/
15
+ .ruff_cache/
16
+ .coverage
17
+ htmlcov/
@@ -0,0 +1,89 @@
1
+ # CLAUDE.md
2
+
3
+ Guidance for working in this repository.
4
+
5
+ ## What this is
6
+
7
+ `pi-py-sdk` is a Python SDK that drives **Pi**'s agent runtime (`pi-agent-core`, written
8
+ in TypeScript) over Pi's **RPC mode** (`pi --mode rpc`, strict JSONL over stdin/stdout),
9
+ plus a terminal coding agent (`pi-py`) built on that SDK.
10
+
11
+ **Core principle: no agent logic is reimplemented in Python.** The agent loop, tool
12
+ calling, sessions, compaction, retries, and provider auth all run inside the `pi`
13
+ subprocess. This package is a transport + protocol + ergonomics layer over that
14
+ subprocess. When extending it, prefer exposing an existing Pi RPC command/event over
15
+ adding behavior here.
16
+
17
+ ## Layout
18
+
19
+ ```
20
+ src/pi_py_sdk/ # the SDK (the bridge)
21
+ jsonl.py # strict LF-only JSONL framing (mirrors Pi's jsonl.ts)
22
+ transport.py # async subprocess lifecycle, stdout framing, stderr ring buffer
23
+ protocol.py # Pydantic models: commands, responses, events, messages
24
+ client.py # PiAgent — async API, id-correlated requests, prompt streaming
25
+ sync.py # PiAgentSync — blocking facade over a background-thread loop
26
+ config.py # PiConfig (CLI args + env)
27
+ _discovery.py # resolve `pi`: PATH -> npx fallback (pinned version)
28
+ errors.py # PiError hierarchy
29
+ src/pi_py_agent/ # the terminal coding agent (consumes the SDK only)
30
+ render.py # stream events -> terminal (text/thinking/tool/queue)
31
+ app.py # async REPL + one-shot runner, mid-turn steering, approvals
32
+ cli.py # `pi-py` console entry point
33
+ tests/ # pytest; unit tests need no Node (fake transports)
34
+ docs/python-sdk-plan.md # full design + roadmap
35
+ ```
36
+
37
+ ## Non-obvious invariants — read before editing the protocol
38
+
39
+ - **JSONL framing is LF-only.** Split stdout on `\n` only; never on U+2028/U+2029 (valid
40
+ inside JSON strings). Strip a single trailing `\r`. See `jsonl.py`; this must stay
41
+ byte-compatible with Pi's `packages/coding-agent/src/modes/rpc/jsonl.ts`.
42
+ - **A `prompt` response means preflight succeeded, not that the turn finished.**
43
+ Completion is the `agent_end` event — and only when `agent_end.willRetry == False`.
44
+ `willRetry == True` is followed by an `auto_retry_*` cycle and another `agent_end`.
45
+ `prompt_stream`/`wait_for_idle` depend on this.
46
+ - **Wire field names are camelCase** (`assistantMessageEvent`, `toolCallId`, `willRetry`,
47
+ `modelId`). Models keep those names verbatim. All models use `extra="allow"` so new Pi
48
+ fields/events degrade gracefully — don't tighten this.
49
+ - **Auth lives in Pi, not here.** The transport forwards `os.environ`; Pi resolves all
50
+ provider keys/OAuth. Never add key handling in Python.
51
+ - **`bash` command vs the bash tool:** the `bash` RPC command stores a
52
+ BashExecutionMessage that is only sent to the LLM on the *next* prompt.
53
+ - **`extension_ui_response` is not id-correlated** — it's written directly to stdin, not
54
+ via the request/response (`_send`) path.
55
+
56
+ ## Dev commands
57
+
58
+ ```bash
59
+ pip install -e ".[dev]"
60
+ pytest # unit tests (no Node; integration deselected via addopts)
61
+ pytest -m integration # live tests; needs `pi` on PATH (no key needed)
62
+ PI_LIVE_LLM=1 pytest -m integration # also runs the prompt-completion test (needs a model)
63
+ python -m build # build wheel + sdist
64
+ ```
65
+
66
+ There is no linter/formatter configured. Match the surrounding style: 4-space indent,
67
+ type hints, `from __future__ import annotations`, Google-ish docstrings.
68
+
69
+ ## Testing approach
70
+
71
+ Unit tests inject a **fake transport** (see `tests/test_client_fake.py`,
72
+ `test_ui_and_events.py`, `test_commands.py`) or a fake agent (`test_sync.py`) so nothing
73
+ spawns Node. Pure helpers (framing, event/message parsing, REPL routing, rendering) are
74
+ tested directly. Live behavior goes in `tests/test_integration.py` behind the
75
+ `integration` marker (skipped automatically when `pi` is absent).
76
+
77
+ ## Release
78
+
79
+ Version lives in `pyproject.toml` and `src/pi_py_sdk/__init__.py` (keep them in sync).
80
+ CI (`.github/workflows/ci.yml`) runs the unit matrix (3.10–3.13), builds the wheel, and
81
+ best-effort-smokes a real `pi`. Publishing to PyPI happens on a GitHub Release via
82
+ `.github/workflows/publish.yml` using Trusted Publishing (OIDC, no token). To cut a
83
+ release: bump the version, then `gh release create vX.Y.Z --generate-notes`.
84
+
85
+ ## Git
86
+
87
+ Work on a feature branch and fast-forward into `main`; `main` tracks
88
+ `origin` (github.com/noclaw/pi-py). End commit messages with the
89
+ `Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>` trailer.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 NoClaw.ai
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.
@@ -0,0 +1,155 @@
1
+ Metadata-Version: 2.4
2
+ Name: pi-py-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the Pi coding agent (pi-agent-core) over its RPC bridge
5
+ Project-URL: Homepage, https://github.com/earendil-works/pi
6
+ Author: Jeff Jacobsen
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Keywords: agent,coding-agent,llm,pi,rpc
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: pydantic<3,>=2.6
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
24
+ Requires-Dist: pytest>=8; extra == 'dev'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # pi-py-sdk
28
+
29
+ [![CI](https://github.com/noclaw/pi-py/actions/workflows/ci.yml/badge.svg)](https://github.com/noclaw/pi-py/actions/workflows/ci.yml)
30
+
31
+ Python SDK for the [Pi](https://pi.dev) coding agent. It drives `pi-agent-core` — the
32
+ well-tested TypeScript agent runtime — over Pi's **RPC mode** (`pi --mode rpc`, strict
33
+ JSONL over stdin/stdout), so the agent loop, tool calling, sessions, compaction,
34
+ retries, and provider auth all run inside Pi. No agent logic is reimplemented in Python.
35
+
36
+ It includes the bridge core (transport, strict JSONL framing, id-correlated commands,
37
+ streaming), the full RPC command surface, typed events and message models, the
38
+ interactive **extension-UI sub-protocol** (tool approvals/dialogs), and a synchronous
39
+ facade (`PiAgentSync`). A terminal coding agent (`pi-py`) ships on top. See
40
+ [`docs/python-sdk-plan.md`](docs/python-sdk-plan.md) for the design.
41
+
42
+ ## Install
43
+
44
+ ```bash
45
+ pip install pi-py-sdk
46
+ ```
47
+
48
+ This installs the `pi_py_sdk` library and the `pi-py` agent CLI. You also need the Pi
49
+ runtime for live use:
50
+
51
+ ```bash
52
+ npm i -g @earendil-works/pi-coding-agent # provides the `pi` binary
53
+ export ANTHROPIC_API_KEY=... # or another supported provider key
54
+ ```
55
+
56
+ If `pi` isn't on `PATH`, the SDK falls back to `npx --yes @earendil-works/pi-coding-agent@<pinned>`.
57
+
58
+ ### Development
59
+
60
+ ```bash
61
+ python -m venv .venv && source .venv/bin/activate
62
+ pip install -e ".[dev]"
63
+ pytest
64
+ ```
65
+
66
+ ## Usage
67
+
68
+ ```python
69
+ import asyncio
70
+ from pi_py_sdk import PiAgent, MessageUpdateEvent
71
+
72
+ async def main():
73
+ async with PiAgent(model="anthropic/claude-sonnet-4-20250514", cwd=".") as agent:
74
+ async for ev in agent.prompt_stream("List the Python files here"):
75
+ if isinstance(ev, MessageUpdateEvent) and ev.assistantMessageEvent:
76
+ ame = ev.assistantMessageEvent
77
+ if ame.type == "text_delta" and ame.delta:
78
+ print(ame.delta, end="", flush=True)
79
+
80
+ asyncio.run(main())
81
+ ```
82
+
83
+ A prompt completes on an `agent_end` event with `willRetry == False` (an `agent_end`
84
+ with `willRetry == True` is followed by an automatic retry).
85
+
86
+ ### Synchronous use
87
+
88
+ For non-async code, `PiAgentSync` runs the agent on a background loop and blocks:
89
+
90
+ ```python
91
+ from pi_py_sdk import PiAgentSync, message_text
92
+
93
+ with PiAgentSync(model="anthropic/claude-sonnet-4-20250514") as agent:
94
+ for event in agent.prompt_stream("hello"):
95
+ ...
96
+ for msg in agent.get_messages(): # typed messages
97
+ print(msg.role, message_text(msg))
98
+ ```
99
+
100
+ ### Tool approvals
101
+
102
+ Extensions request decisions (allow this tool? pick an option? enter a value?) via the
103
+ extension-UI sub-protocol. Install a handler with `on_ui_request`; without one, the SDK
104
+ safely denies confirmations and cancels other dialogs so the agent never hangs.
105
+
106
+ ```python
107
+ def approve(req):
108
+ if req.method == "confirm":
109
+ return True # allow
110
+ if req.method == "select":
111
+ return (req.options or [None])[0]
112
+ return None # cancel input/editor
113
+
114
+ agent.on_ui_request(approve) # see examples/with_approvals.py
115
+ ```
116
+
117
+ The full command surface (`set_model`, `bash`, `compact`, `fork`, `get_session_stats`,
118
+ steering/follow-up modes, …) is available as async methods on `PiAgent`.
119
+
120
+ ## The `pi-py` coding agent
121
+
122
+ The repo also ships `pi_py_agent`, a small terminal coding agent built entirely on the
123
+ SDK (the agent loop, tools, and model calls all run inside Pi). Installing the package
124
+ provides a `pi-py` command:
125
+
126
+ ```bash
127
+ pi-py # interactive REPL
128
+ pi-py --print "Run the tests and summarize failures" # one-shot
129
+ pi-py --model anthropic/claude-sonnet-4-20250514 --no-session
130
+ ```
131
+
132
+ It streams assistant text, thinking, and tool activity (with result previews) to the
133
+ terminal, answers approval dialogs interactively, and supports slash commands (`/help`,
134
+ `/model`, `/models`, `/new`, `/state`, `/compact`, `/clone`, `/fork`, `/exit`). While
135
+ the agent is responding you can **steer** it by typing (or `+text` to queue a
136
+ follow-up). Ctrl-C aborts the current turn; Ctrl-D exits.
137
+
138
+ ## Tests
139
+
140
+ ```bash
141
+ pytest # unit tests (no Node required); integration is deselected by default
142
+ pytest -m integration # live tests against a real `pi` (needs the binary on PATH)
143
+ ```
144
+
145
+ The integration tests avoid LLM calls (state, models, bash), so they don't need a
146
+ provider key. The one prompt-completion test additionally needs a working model and is
147
+ skipped unless `PI_LIVE_LLM=1` is set.
148
+
149
+ ## Releasing
150
+
151
+ CI (`.github/workflows/ci.yml`) runs the unit suite across Python 3.10–3.13, builds the
152
+ wheel, and best-effort-smokes a real `pi` on every push/PR. Publishing
153
+ (`.github/workflows/publish.yml`) builds and uploads to PyPI when a GitHub Release is
154
+ published — it uses [PyPI Trusted Publishing](https://docs.pypi.org/trusted-publishers/)
155
+ (OIDC, no token secret), which must be configured once for the repo.
@@ -0,0 +1,129 @@
1
+ # pi-py-sdk
2
+
3
+ [![CI](https://github.com/noclaw/pi-py/actions/workflows/ci.yml/badge.svg)](https://github.com/noclaw/pi-py/actions/workflows/ci.yml)
4
+
5
+ Python SDK for the [Pi](https://pi.dev) coding agent. It drives `pi-agent-core` — the
6
+ well-tested TypeScript agent runtime — over Pi's **RPC mode** (`pi --mode rpc`, strict
7
+ JSONL over stdin/stdout), so the agent loop, tool calling, sessions, compaction,
8
+ retries, and provider auth all run inside Pi. No agent logic is reimplemented in Python.
9
+
10
+ It includes the bridge core (transport, strict JSONL framing, id-correlated commands,
11
+ streaming), the full RPC command surface, typed events and message models, the
12
+ interactive **extension-UI sub-protocol** (tool approvals/dialogs), and a synchronous
13
+ facade (`PiAgentSync`). A terminal coding agent (`pi-py`) ships on top. See
14
+ [`docs/python-sdk-plan.md`](docs/python-sdk-plan.md) for the design.
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ pip install pi-py-sdk
20
+ ```
21
+
22
+ This installs the `pi_py_sdk` library and the `pi-py` agent CLI. You also need the Pi
23
+ runtime for live use:
24
+
25
+ ```bash
26
+ npm i -g @earendil-works/pi-coding-agent # provides the `pi` binary
27
+ export ANTHROPIC_API_KEY=... # or another supported provider key
28
+ ```
29
+
30
+ If `pi` isn't on `PATH`, the SDK falls back to `npx --yes @earendil-works/pi-coding-agent@<pinned>`.
31
+
32
+ ### Development
33
+
34
+ ```bash
35
+ python -m venv .venv && source .venv/bin/activate
36
+ pip install -e ".[dev]"
37
+ pytest
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ```python
43
+ import asyncio
44
+ from pi_py_sdk import PiAgent, MessageUpdateEvent
45
+
46
+ async def main():
47
+ async with PiAgent(model="anthropic/claude-sonnet-4-20250514", cwd=".") as agent:
48
+ async for ev in agent.prompt_stream("List the Python files here"):
49
+ if isinstance(ev, MessageUpdateEvent) and ev.assistantMessageEvent:
50
+ ame = ev.assistantMessageEvent
51
+ if ame.type == "text_delta" and ame.delta:
52
+ print(ame.delta, end="", flush=True)
53
+
54
+ asyncio.run(main())
55
+ ```
56
+
57
+ A prompt completes on an `agent_end` event with `willRetry == False` (an `agent_end`
58
+ with `willRetry == True` is followed by an automatic retry).
59
+
60
+ ### Synchronous use
61
+
62
+ For non-async code, `PiAgentSync` runs the agent on a background loop and blocks:
63
+
64
+ ```python
65
+ from pi_py_sdk import PiAgentSync, message_text
66
+
67
+ with PiAgentSync(model="anthropic/claude-sonnet-4-20250514") as agent:
68
+ for event in agent.prompt_stream("hello"):
69
+ ...
70
+ for msg in agent.get_messages(): # typed messages
71
+ print(msg.role, message_text(msg))
72
+ ```
73
+
74
+ ### Tool approvals
75
+
76
+ Extensions request decisions (allow this tool? pick an option? enter a value?) via the
77
+ extension-UI sub-protocol. Install a handler with `on_ui_request`; without one, the SDK
78
+ safely denies confirmations and cancels other dialogs so the agent never hangs.
79
+
80
+ ```python
81
+ def approve(req):
82
+ if req.method == "confirm":
83
+ return True # allow
84
+ if req.method == "select":
85
+ return (req.options or [None])[0]
86
+ return None # cancel input/editor
87
+
88
+ agent.on_ui_request(approve) # see examples/with_approvals.py
89
+ ```
90
+
91
+ The full command surface (`set_model`, `bash`, `compact`, `fork`, `get_session_stats`,
92
+ steering/follow-up modes, …) is available as async methods on `PiAgent`.
93
+
94
+ ## The `pi-py` coding agent
95
+
96
+ The repo also ships `pi_py_agent`, a small terminal coding agent built entirely on the
97
+ SDK (the agent loop, tools, and model calls all run inside Pi). Installing the package
98
+ provides a `pi-py` command:
99
+
100
+ ```bash
101
+ pi-py # interactive REPL
102
+ pi-py --print "Run the tests and summarize failures" # one-shot
103
+ pi-py --model anthropic/claude-sonnet-4-20250514 --no-session
104
+ ```
105
+
106
+ It streams assistant text, thinking, and tool activity (with result previews) to the
107
+ terminal, answers approval dialogs interactively, and supports slash commands (`/help`,
108
+ `/model`, `/models`, `/new`, `/state`, `/compact`, `/clone`, `/fork`, `/exit`). While
109
+ the agent is responding you can **steer** it by typing (or `+text` to queue a
110
+ follow-up). Ctrl-C aborts the current turn; Ctrl-D exits.
111
+
112
+ ## Tests
113
+
114
+ ```bash
115
+ pytest # unit tests (no Node required); integration is deselected by default
116
+ pytest -m integration # live tests against a real `pi` (needs the binary on PATH)
117
+ ```
118
+
119
+ The integration tests avoid LLM calls (state, models, bash), so they don't need a
120
+ provider key. The one prompt-completion test additionally needs a working model and is
121
+ skipped unless `PI_LIVE_LLM=1` is set.
122
+
123
+ ## Releasing
124
+
125
+ CI (`.github/workflows/ci.yml`) runs the unit suite across Python 3.10–3.13, builds the
126
+ wheel, and best-effort-smokes a real `pi` on every push/PR. Publishing
127
+ (`.github/workflows/publish.yml`) builds and uploads to PyPI when a GitHub Release is
128
+ published — it uses [PyPI Trusted Publishing](https://docs.pypi.org/trusted-publishers/)
129
+ (OIDC, no token secret), which must be configured once for the repo.