docs: restructure docs into diataxis (#121)

This commit is contained in:
banteg
2026-01-13 15:59:27 +04:00
committed by GitHub
parent d0e9a51a0f
commit e292c99ab0
52 changed files with 1538 additions and 1255 deletions
+133
View File
@@ -0,0 +1,133 @@
Below is a concrete implementation spec for the **Pi (pi-coding-agent CLI)** runner shipped in Takopi (v0.5.0).
---
## Scope
### Goal
Provide the **`pi`** engine backend so Takopi can:
* Run Pi non-interactively via the **pi CLI** (`pi --print`).
* Stream progress by parsing **`--mode json`** (newline-delimited JSON). Each line is a JSON object.
* Support resumable sessions via **`--session <path>`** (Takopi emits a canonical resume line the user can reply with).
### Non-goals (v1)
* Interactive TUI flows (session picker, prompts, etc.)
* RPC mode (requires a long-running process and JSON commands)
---
## UX and behavior
### Engine selection
* Default: `takopi` (auto-router uses `default_engine` from config)
* Override: `takopi pi`
### Resume UX (canonical line)
Takopi appends a **single backticked** resume line at the end of the message, like:
```text
`pi --session ccd569e0`
```
Notes:
* `pi --resume/-r` opens an interactive session picker, so Takopi uses `--session <path>` instead.
* The resume token is the **session id** (short prefix), derived from the first JSON
object in the session file. If the id cannot be read, Takopi falls back to the
session file path.
* If the path contains spaces, the runner will quote it.
### Non-interactive runs
Use `--print` and `--mode json` for headless JSONL output.
Pi does not accept `-- <prompt>` to protect prompts starting with `-`. Takopi prefixes a leading space if the prompt begins with `-` so it is not parsed as a flag.
---
## Config additions
Takopi config lives at `~/.takopi/takopi.toml`.
Add a new optional `[pi]` section.
Recommended schema:
```toml
# ~/.takopi/takopi.toml
default_engine = "pi"
[pi]
model = "..." # optional; passed as --model
provider = "..." # optional; passed as --provider
extra_args = [] # optional list of strings, appended verbatim
```
Notes:
* `extra_args` lets you pass new Pi flags without changing Takopi.
* Session files are stored under Pi's default session dir:
`~/.pi/agent/sessions/--<cwd>--` (with path separators replaced by `-`).
---
## Code changes (by file)
### 1) New file: `src/takopi/runners/pi.py`
Expose a module-level `BACKEND = EngineBackend(...)`.
#### Runner invocation
The runner should launch Pi in headless JSON mode:
```text
pi --print --mode json --session <session.jsonl> <prompt>
```
When resuming, `<session.jsonl>` is the resume token extracted from the chat.
#### Event translation
Pi JSONL output is `AgentSessionEvent` (from `@mariozechner/pi-agent-core`).
The runner should translate:
* `tool_execution_start` -> `action` (phase: started)
* `tool_execution_end` -> `action` (phase: completed)
* `agent_end` -> `completed`
For the final answer, use the most recent assistant message text (from
`message_end` events). For errors, if the assistant stopReason is `error` or
`aborted`, emit `completed(ok=false, error=...)`.
---
## Installation and auth
Install the CLI globally:
```text
npm install -g @mariozechner/pi-coding-agent
```
Auth is stored under `~/.pi/agent/auth.json`. Run `pi` once interactively to
set up credentials before using Takopi.
---
## Known pitfalls
* `--resume` is interactive; Takopi uses `--session <path>` instead.
* Prompts that start with `-` are interpreted as flags by the CLI. Takopi
prefixes a space to make them safe.
---
If you want, I can also add a sample `takopi.toml` snippet to the README or
include a small quickstart section for Pi in the onboarding panel.
@@ -0,0 +1,67 @@
# Pi `--mode json` event cheatsheet
`pi --print --mode json` writes **one JSON object per line** (JSONL) with a
required `type` field. These are `AgentSessionEvent` objects from
`@mariozechner/pi-agent-core`.
## Top-level event lines
### `agent_start`
```json
{"type":"agent_start"}
```
### `agent_end`
```json
{"type":"agent_end","messages":[{"role":"assistant","content":[{"type":"text","text":"Done."}],"stopReason":"stop","timestamp":123}]}
```
### `turn_start` / `turn_end`
```json
{"type":"turn_start"}
```
```json
{"type":"turn_end","message":{...},"toolResults":[...]}
```
### `message_start` / `message_update` / `message_end`
```json
{"type":"message_start","message":{"role":"assistant","content":[{"type":"text","text":"Working..."}]}}
```
```json
{"type":"message_update","message":{...},"assistantMessageEvent":{"type":"text_delta","delta":"...","contentIndex":0}}
```
```json
{"type":"message_end","message":{"role":"assistant","content":[{"type":"text","text":"Done."}],"stopReason":"stop"}}
```
### `tool_execution_start`
```json
{"type":"tool_execution_start","toolCallId":"tool_1","toolName":"bash","args":{"command":"ls"}}
```
### `tool_execution_update`
```json
{"type":"tool_execution_update","toolCallId":"tool_1","toolName":"bash","args":{"command":"ls"},"partialResult":{"content":[{"type":"text","text":"..."}]}}
```
### `tool_execution_end`
```json
{"type":"tool_execution_end","toolCallId":"tool_1","toolName":"bash","result":{"content":[{"type":"text","text":"ok"}],"details":{}},"isError":false}
```
## Notes
* `message_end` with `role = "assistant"` contains the final assistant text.
* `assistantMessageEvent` in `message_update` provides streaming deltas.
* `tool_execution_*` events map cleanly to Takopi `action` events.
+152
View File
@@ -0,0 +1,152 @@
# Pi -> Takopi event mapping (spec)
This document describes how the Pi runner translates Pi CLI `--mode json` JSONL events into Takopi events.
> **Authoritative source:** The schema definitions are in `src/takopi/schemas/pi.py` and the translation logic is in `src/takopi/runners/pi.py`. When in doubt, refer to the code.
The goal is to make Pi feel identical to the Codex/Claude runners from the bridge/renderer point of view while preserving Takopi invariants (stable action ids, per-session serialization, single completed event).
---
## 1. Input stream contract (Pi CLI)
Pi CLI emits **one JSON object per line** (JSONL) when invoked with:
```
pi --print --mode json <prompt>
```
Notes:
- `--print` is required for non-interactive runs.
- `--mode json` outputs all agent events (no TUI banners).
- Pi does not support `-- <prompt>`; prompts starting with `-` must be
prefixed (Takopi does this automatically).
---
## 2. Resume tokens and resume lines
- Engine id: `pi`
- Canonical resume line (embedded in chat):
```
`pi --session <id>`
```
The token is the **short session id**, derived from the first JSON object in the
session file. If the id cannot be read, Takopi falls back to the session file path.
Why not `--resume`?
- `--resume/-r` opens an interactive session picker; it does not accept a
session token. Takopi must use `--session <path>` instead.
---
## 3. Session lifecycle + serialization
Takopi requires **serialization per session token**:
- For new runs (`resume=None`), do **not** acquire a lock until a `started`
event is emitted (Takopi emits this as soon as the first JSON event arrives).
- Once the session is known, acquire a lock for `pi:<session_path>` and hold it
until the run completes.
- For resumed runs, acquire the lock immediately on entry.
---
## 4. Event translation (Pi JSONL -> Takopi)
Pi emits `AgentSessionEvent` objects. Only a subset is required for Takopi.
### 4.1 `tool_execution_start`
Example:
```json
{"type":"tool_execution_start","toolCallId":"tool_1","toolName":"bash","args":{"command":"ls"}}
```
Mapping:
- Emit `action` with `phase="started"`.
- `action.id = toolCallId`.
- `action.kind` from tool name (see section 5).
- `action.title` derived from tool + args.
### 4.2 `tool_execution_end`
Example:
```json
{"type":"tool_execution_end","toolCallId":"tool_1","toolName":"bash","result":{...},"isError":false}
```
Mapping:
- Emit `action` with `phase="completed"`.
- `ok = !isError`.
- Carry `result` and `isError` in `detail` for debugging.
### 4.3 `message_end` (assistant)
Pi emits message lifecycle events. For `message_end` where `message.role == "assistant"`:
- Store the latest assistant text as the **final answer fallback**.
- If `stopReason` is `error` or `aborted`, store `errorMessage`.
- Capture `usage` for `completed.usage`.
### 4.4 `agent_end`
Example:
```json
{"type":"agent_end","messages":[...]}
```
Mapping:
- Emit a single `completed` event:
- `ok = true` unless the last assistant message has `stopReason` `error` or `aborted`.
- `answer = last assistant text` (from `message_end` or `agent_end.messages`).
- `error = errorMessage` if present.
- `resume = ResumeToken(engine="pi", value=session_path)`.
- `usage = last assistant usage`.
### 4.5 Other events
Ignore unknown events. If a JSONL line is malformed, emit a warning action and
continue (default `JsonlSubprocessRunner` behavior).
---
## 5. Tool name -> ActionKind mapping heuristics
Pi tool names are lower-case by default. Suggested mapping:
| Tool name | ActionKind | Title logic |
| --- | --- | --- |
| `bash` | `command` | `args.command` |
| `edit`, `write` | `file_change` | `args.path` |
| `read` | `tool` | `read: <path>` |
| `grep` | `tool` | `grep: <pattern>` |
| `find` | `tool` | `find: <pattern>` |
| `ls` | `tool` | `ls: <path>` |
| (default) | `tool` | tool name |
For `file_change`, include `detail.changes = [{"path": <path>, "kind": "update"}]`.
---
## 6. Usage mapping
Takopi `completed.usage` should mirror Pi's assistant `usage` object without
transformation.
---
## 7. Suggested Takopi config keys
A minimal TOML config for Pi:
```toml
[pi]
model = "..."
provider = "..."
extra_args = []
```
Use `extra_args` for any Pi CLI flags not explicitly mapped.