docs: move documentation to the repository root

The docs cover more than the coding agent, so they move from
packages/coding-agent/docs to docs/. Links into coding-agent files now use
the repository layout (../packages/coding-agent/examples/...).

The coding-agent build copies docs/ into packages/coding-agent/docs/
(generated, gitignored) with scripts/copy-docs.mjs and rewrites links into
the package back to package-relative paths. npm, the release binaries, Nix
and the evals image pack after the build, so releases keep shipping
<package>/docs unchanged. getDocsPath() returns the repository docs/ in
source checkouts.
This commit is contained in:
Christian Klotz 2026-10-14 02:51:22 +01:00
parent 482c1381a0
commit 91a7019fce
No known key found for this signature in database
60 changed files with 140 additions and 71 deletions

2
.gitignore vendored
View file

@ -37,6 +37,8 @@ out.jsonl
pi-*.html
out.html
packages/coding-agent/binaries/
# Generated from docs/ by the coding-agent build (scripts/copy-docs.mjs)
packages/coding-agent/docs/
packages/env/bin/
packages/env/daemon/target/
todo.md

View file

@ -10,7 +10,7 @@ For each PR URL, do the following in order:
3. Identify any linked issues referenced in the PR body, comments, commit messages, or cross links. Read each issue in full, including all comments.
4. Analyze the PR diff without checking out or switching to the PR branch. Use `gh pr diff`, `gh pr view`, `gh api`, and local main-branch files; if PR file contents are needed, use fetched refs with `git show <ref>:<path>` or temporary files. Read all relevant code files in full with no truncation and compare against the diff. Do not fetch PR file blobs unless a file is missing on main or the diff context is insufficient. Include related code paths that are not in the diff but are required to validate behavior.
5. Do not check for a changelog entry. Per CONTRIBUTING.md, contributor PRs must not edit `CHANGELOG.md` — the maintainer adds the entry when merging.
6. Check if packages/coding-agent/README.md, packages/coding-agent/docs/*.md, packages/coding-agent/examples/**/*.md require modification. This is usually the case when existing features have been changed, or new features have been added.
6. Check if packages/coding-agent/README.md, docs/*.md, packages/coding-agent/examples/**/*.md require modification. This is usually the case when existing features have been changed, or new features have been added.
7. Provide a structured review with these sections:
- What it does: one short paragraph describing the change and its intent.
- Good: solid choices or improvements.

View file

@ -49,7 +49,7 @@ Create a provider file exporting:
- `src/core/provider-display-names.ts`: add API-key login display name so `/login` and related UI show the provider for built-in API-key auth.
- `src/cli/args.ts`: add env var documentation.
- `README.md`: add provider setup instructions.
- `docs/providers.md`: add setup instructions, env var, and `auth.json` key.
- `docs/providers.md` (repository root): add setup instructions, env var, and `auth.json` key.
## 7. Documentation

View file

@ -14,11 +14,11 @@
Pi is a minimal, extensible agent harness that you can make your own.
Adapt Pi to your workflows, not the other way around. Customize Pi with [extensions](packages/coding-agent/docs/extensions.md), [skills](packages/coding-agent/docs/skills.md), [prompt templates](packages/coding-agent/docs/prompt-templates.md), and [themes](packages/coding-agent/docs/themes.md). Bundle them as [Pi packages](packages/coding-agent/docs/packages.md) and share via npm or git.
Adapt Pi to your workflows, not the other way around. Customize Pi with [extensions](docs/extensions.md), [skills](docs/skills.md), [prompt templates](docs/prompt-templates.md), and [themes](docs/themes.md). Bundle them as [Pi packages](docs/packages.md) and share via npm or git.
Pi ships with powerful defaults but skips features like sub-agents and plan mode. Ask Pi to build what you want, or install a package that does it your way.
Use Pi [interactively](packages/coding-agent/docs/usage.md), automate it in [print or JSON mode](packages/coding-agent/docs/cli.md), control it over [RPC](packages/coding-agent/docs/rpc.md), or build apps with the [Pi TypeScript SDK](packages/coding-agent/docs/sdk.md). See [OpenClaw](https://github.com/OpenClaw/OpenClaw) for a real-world integration.
Use Pi [interactively](docs/usage.md), automate it in [print or JSON mode](docs/cli.md), control it over [RPC](docs/rpc.md), or build apps with the [Pi TypeScript SDK](docs/sdk.md). See [OpenClaw](https://github.com/OpenClaw/OpenClaw) for a real-world integration.
## Getting started
@ -89,7 +89,7 @@ For Slack/chat automation and workflows see [earendil-works/pi-chat](https://git
Pi does not include a built-in permission system for restricting filesystem, process, network, or credential access. By default, it runs with the permissions of the user and process that launched it.
If you need stronger boundaries, containerize or sandbox Pi. See [packages/coding-agent/docs/containerization.md](packages/coding-agent/docs/containerization.md) for three patterns:
If you need stronger boundaries, containerize or sandbox Pi. See [docs/containerization.md](docs/containerization.md) for three patterns:
- **Gondolin extension**: keep `pi` and provider auth on the host while routing built-in tools and `!` commands into a local Linux micro-VM.
- **Plain Docker**: run the whole `pi` process in a local container for simple isolation.
@ -202,5 +202,5 @@ MIT
<p align="center">
<a href="https://pi.dev">pi.dev</a> domain graciously donated by
<br /><br />
<a href="https://exe.dev"><img src="packages/coding-agent/docs/images/exy.png" alt="Exy mascot" width="48" /><br />exe.dev</a>
<a href="https://exe.dev"><img src="docs/images/exy.png" alt="Exy mascot" width="48" /><br />exe.dev</a>
</p>

View file

@ -71,7 +71,7 @@ Extension dialogs form a request-response subprotocol. Other extension UI update
For Node.js or TypeScript integrations, prefer `RpcClient` from `@earendil-works/pi-coding-agent`. It starts a Pi RPC child process, correlates requests, exposes typed command methods, and delivers session events to listeners.
The [RPC client example](../examples/rpc-client.ts) sends one prompt, streams text and tool activity, waits for `agent_settled`, and shuts down the child process. It is included in the repository’s TypeScript checks.
The [RPC client example](../packages/coding-agent/examples/rpc-client.ts) sends one prompt, streams text and tool activity, waits for `agent_settled`, and shuts down the child process. It is included in the repository’s TypeScript checks.
`RpcClient.promptAndWait()` installs its event listener before sending the prompt, avoiding a race with fast completions. For separate operations, subscribe before calling `prompt()` and call `waitForIdle()` only while a run is active.
@ -96,11 +96,11 @@ Change the top-level `bin` field to set the executable name. These settings affe
## Examples and references
- [RPC client](../examples/rpc-client.ts): typed Node.js integration
- [RPC extension UI](../examples/rpc-extension-ui.ts): custom terminal client with extension dialogs
- [RPC client](../packages/coding-agent/examples/rpc-client.ts): typed Node.js integration
- [RPC extension UI](../packages/coding-agent/examples/rpc-extension-ui.ts): custom terminal client with extension dialogs
- [Command Line](cli.md): startup options and mode selection
- [JSON Event Stream](json.md): JSON event reference
- [RPC Protocol](rpc.md): RPC lifecycle, framing, errors, and shutdown
- [RPC Commands](rpc-commands.md): command and response reference
- [RPC Extension UI](rpc-extension-ui.md): extension interaction subprotocol
- [SDK examples](../examples/sdk/): in-process TypeScript integrations
- [SDK examples](../packages/coding-agent/examples/sdk/): in-process TypeScript integrations

View file

@ -365,7 +365,7 @@ pi.on("session_before_compact", async (event, ctx) => {
});
```
See [custom-compaction.ts](../examples/extensions/custom-compaction.ts) for a complete example using a different model.
See [custom-compaction.ts](../packages/coding-agent/examples/extensions/custom-compaction.ts) for a complete example using a different model.
### session_compact_failed

View file

@ -180,4 +180,4 @@ pi -e ~/.pi/agent/extensions/gondolin
The extension mounts the host working folder at `/workspace` in the VM and overrides `read`, `write`, `edit`, `bash`, `grep`, `find`, and `ls`. File changes under `/workspace` write through to the host.
Other extension tools still run on the host unless they explicitly delegate their operations. Review the [Gondolin example](../examples/extensions/gondolin/) before adding tools that could bypass the VM boundary.
Other extension tools still run on the host unless they explicitly delegate their operations. Review the [Gondolin example](../packages/coding-agent/examples/extensions/gondolin/) before adding tools that could bypass the VM boundary.

View file

@ -121,7 +121,7 @@ Model-level `baseUrl` values take precedence over the provider endpoint. If no `
Calls made after initial extension loading take effect immediately. Use `pi.unregisterProvider()` to remove the dynamic provider and restore built-in behavior that it replaced.
See the checked [GitLab Duo provider](../examples/extensions/custom-provider-gitlab-duo/) for a complete registration that delegates streaming to built-in API implementations.
See the checked [GitLab Duo provider](../packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/) for a complete registration that delegates streaming to built-in API implementations.
## Provide authentication

View file

@ -204,7 +204,7 @@ Notification handlers should not perform slow work on a streaming event. Transfo
`provider_stream_event` fires for each parsed provider stream event before Pi normalizes it. The event identifies the provider, API, and model; `event.data` is the earliest structured value available to Pi, not necessarily the original HTTP bytes or SSE frame. Treat it as read-only because mutation can affect normalization. The event is notification-only and is not persisted.
Handlers are awaited in stream order, so slow handlers delay stream consumption. Handler errors are reported without changing the provider response. See [`debug-provider.ts`](../examples/extensions/debug-provider.ts) for an opt-in viewer that groups raw events by assistant message.
Handlers are awaited in stream order, so slow handlers delay stream consumption. Handler errors are reported without changing the provider response. See [`debug-provider.ts`](../packages/coding-agent/examples/extensions/debug-provider.ts) for an opt-in viewer that groups raw events by assistant message.
<a id="context_with_system"></a>
@ -243,7 +243,7 @@ Declare `outputSchema` and return a matching `structuredContent` when the result
A tool can run other tools with `ctx.executeTool(name, args, { signal, onUpdate })`. Nested calls go through argument validation and the `tool_call` and `tool_result` handlers like model-issued calls, and emit `tool_execution_start`, `tool_execution_update`, and `tool_execution_end`; all of these events carry `parentToolCallId`, and their `toolCallId` is assigned by pi as `<parent id>/<n>`. These ids do not appear as tool calls or tool results in the transcript. Nested calls do not add transcript entries: their results only reach the calling tool, which reports them itself, for example through `onUpdate` and `details`. The session keeps a bounded record of them (name, arguments, status, duration, error; never results) as `nestedCalls` on the calling tool's result message. It is used for compaction file lists and shown in HTML exports. Arguments over 8 KiB per call or 32 KiB per tool result are omitted, at most 256 calls are kept, and `complete: false` marks a record that lost anything. The `usage` of nested results, at every depth, is added to the calling tool's result `usage`, so a tool reports only its own usage, not that of the tools it called. `ctx.tools` lists the tools `ctx.executeTool()` can call. `tool_result` handlers that redact `content` should also replace `structuredContent`; replacing only `content` drops it.
See [`hello.ts`](../examples/extensions/hello.ts), [`todo.ts`](../examples/extensions/todo.ts), [`dynamic-tools.ts`](../examples/extensions/dynamic-tools.ts), and [`truncated-tool.ts`](../examples/extensions/truncated-tool.ts).
See [`hello.ts`](../packages/coding-agent/examples/extensions/hello.ts), [`todo.ts`](../packages/coding-agent/examples/extensions/todo.ts), [`dynamic-tools.ts`](../packages/coding-agent/examples/extensions/dynamic-tools.ts), and [`truncated-tool.ts`](../packages/coding-agent/examples/extensions/truncated-tool.ts).
### Tool exposure
@ -366,18 +366,18 @@ Use `ctx.shutdown()` to request an orderly process shutdown.
## Examples and reference
The checked [extension examples](../examples/extensions/) are the implementation reference. Start with the smallest example matching the behavior you need:
The checked [extension examples](../packages/coding-agent/examples/extensions/) are the implementation reference. Start with the smallest example matching the behavior you need:
| Goal | Start with | Why |
|---|---|---|
| Confirm or block tool calls | [`permission-gate.ts`](../examples/extensions/permission-gate.ts) | Combines `tool_call`, mode-aware UI, and fail-closed behavior. |
| Modify prompt sections for a run | [`prompt-customizer.ts`](../examples/extensions/prompt-customizer.ts) | Uses structured prompt options instead of replacing the complete prompt. |
| Keep branch-aware tool state | [`todo.ts`](../examples/extensions/todo.ts) | Reconstructs state from persisted entries and renders it in the TUI. |
| Register tools after startup | [`dynamic-tools.ts`](../examples/extensions/dynamic-tools.ts) | Shares registration logic between lifecycle and command handlers. |
| Run cancellable model work from a command | [`qna.ts`](../examples/extensions/qna.ts) | Combines nested model calls, cancellation, and custom UI. |
| Replace the main editor | [`modal-editor.ts`](../examples/extensions/modal-editor.ts) | Extends `CustomEditor` while preserving application controls. |
| Run finalization at shutdown | [`auto-commit-on-exit.ts`](../examples/extensions/auto-commit-on-exit.ts) | Uses the session shutdown hook for finalization work. |
| Route built-in tools to another machine | [`ssh.ts`](../examples/extensions/ssh.ts) | Reuses tool operation interfaces instead of recreating tool contracts. |
| Add OAuth and provider-specific streaming | [`custom-provider-gitlab-duo/`](../examples/extensions/custom-provider-gitlab-duo/) | Shows a complete provider integration while delegating supported protocols. |
| Confirm or block tool calls | [`permission-gate.ts`](../packages/coding-agent/examples/extensions/permission-gate.ts) | Combines `tool_call`, mode-aware UI, and fail-closed behavior. |
| Modify prompt sections for a run | [`prompt-customizer.ts`](../packages/coding-agent/examples/extensions/prompt-customizer.ts) | Uses structured prompt options instead of replacing the complete prompt. |
| Keep branch-aware tool state | [`todo.ts`](../packages/coding-agent/examples/extensions/todo.ts) | Reconstructs state from persisted entries and renders it in the TUI. |
| Register tools after startup | [`dynamic-tools.ts`](../packages/coding-agent/examples/extensions/dynamic-tools.ts) | Shares registration logic between lifecycle and command handlers. |
| Run cancellable model work from a command | [`qna.ts`](../packages/coding-agent/examples/extensions/qna.ts) | Combines nested model calls, cancellation, and custom UI. |
| Replace the main editor | [`modal-editor.ts`](../packages/coding-agent/examples/extensions/modal-editor.ts) | Extends `CustomEditor` while preserving application controls. |
| Run finalization at shutdown | [`auto-commit-on-exit.ts`](../packages/coding-agent/examples/extensions/auto-commit-on-exit.ts) | Uses the session shutdown hook for finalization work. |
| Route built-in tools to another machine | [`ssh.ts`](../packages/coding-agent/examples/extensions/ssh.ts) | Reuses tool operation interfaces instead of recreating tool contracts. |
| Add OAuth and provider-specific streaming | [`custom-provider-gitlab-duo/`](../packages/coding-agent/examples/extensions/custom-provider-gitlab-duo/) | Shows a complete provider integration while delegating supported protocols. |
Use [Custom Providers](custom-provider.md) for model-service integrations, [Terminal UI](tui.md) for custom components, and [Pi Packages](packages.md) to install or distribute extensions with other resources. Use the exported declarations for exact signatures, payloads, and result schemas; this guide focuses on lifecycle choices, composition, and durable implementation patterns.

View file

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 168 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 507 KiB

After

Width:  |  Height:  |  Size: 507 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 275 KiB

After

Width:  |  Height:  |  Size: 275 KiB

Before After
Before After

View file

@ -195,6 +195,6 @@ Dismiss any dialog method. The extension receives `undefined` (for select/input/
## Example
See the checked [RPC extension UI client](../examples/rpc-extension-ui.ts) and its [demo extension](../examples/extensions/rpc-demo.ts).
See the checked [RPC extension UI client](../packages/coding-agent/examples/rpc-extension-ui.ts) and its [demo extension](../packages/coding-agent/examples/extensions/rpc-demo.ts).
The exported request and response unions are defined in [`rpc-types.ts`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/modes/rpc/rpc-types.ts). See [Extensions](extensions.md#ui-and-modes) for mode-independent extension guidance.

View file

@ -129,7 +129,7 @@ process.stdin.close()
process.wait()
```
For maintained TypeScript clients, use the checked [RPC client example](../examples/rpc-client.ts). It requires a built Pi CLI because the repository example points to `dist/cli.js`.
For maintained TypeScript clients, use the checked [RPC client example](../packages/coding-agent/examples/rpc-client.ts). It requires a built Pi CLI because the repository example points to `dist/cli.js`.
## Reference

View file

@ -19,7 +19,7 @@ try {
This uses the working directory, discovered resources, stored settings, and configured credentials. `prompt()` resolves when the run finishes.
The [complete minimal example](../examples/sdk/01-minimal.ts) also streams text events. All [SDK examples](../examples/sdk/) are typechecked with the repository.
The [complete minimal example](../packages/coding-agent/examples/sdk/01-minimal.ts) also streams text events. All [SDK examples](../packages/coding-agent/examples/sdk/) are typechecked with the repository.
<a id="session-management"></a>
@ -49,7 +49,7 @@ const { session } = await createAgentSession({
});
```
See the checked [sessions example](../examples/sdk/11-sessions.ts) for creating, opening, continuing, listing, and forking sessions. [Session File Format](session-format.md) defines the persisted JSONL contract, and [Message Types](message-types.md) defines transcript values. For exact methods and signatures, use the exported TypeScript declarations or [`session-manager.ts`](../src/core/session-manager.ts).
See the checked [sessions example](../packages/coding-agent/examples/sdk/11-sessions.ts) for creating, opening, continuing, listing, and forking sessions. [Session File Format](session-format.md) defines the persisted JSONL contract, and [Message Types](message-types.md) defines transcript values. For exact methods and signatures, use the exported TypeScript declarations or [`session-manager.ts`](../packages/coding-agent/src/core/session-manager.ts).
`cwd` selects the workspace used for project resource discovery, context files, session grouping, and built-in tool paths. Pass it explicitly when the target differs from `process.cwd()`.
@ -57,7 +57,7 @@ See the checked [sessions example](../examples/sdk/11-sessions.ts) for creating,
`AgentSessionRuntime` adds `newSession()`, `switchSession()`, `fork()`, and `importFromJsonl()`. Each operation replaces the active `AgentSession` and recreates services for the target working directory.
After a runtime replacement, subscriptions belong to the old `AgentSession` and must be rebound. See the [session runtime example](../examples/sdk/13-session-runtime.ts).
After a runtime replacement, subscriptions belong to the old `AgentSession` and must be rebound. See the [session runtime example](../packages/coding-agent/examples/sdk/13-session-runtime.ts).
## Prompting
@ -113,28 +113,28 @@ Inline extension factories can be supplied through `DefaultResourceLoader`. Give
<a id="codemode-mcp"></a>
The CLI loads `codemode`, `tool_search`, and MCP as built-in extensions. SDK sessions do not; add `createCodemodeExtension()`, `createToolSearchExtension()`, and `createMcpExtension()` to the `extensionFactories` of `DefaultResourceLoader`. `codemode` and `tool_search` are registered inactive: enable them through the `defaultTools` setting (`["+codemode", "+tool_search"]` keeps the other default tools), or let the MCP extension activate them: `codemode` for servers with `codemode` exposure, `tool_search` for servers with `deferred` exposure. The MCP extension connects its servers on `session_start`, so call `session.bindExtensions()`. See [Codemode and MCP](../examples/sdk/14-codemode-mcp.ts).
The CLI loads `codemode`, `tool_search`, and MCP as built-in extensions. SDK sessions do not; add `createCodemodeExtension()`, `createToolSearchExtension()`, and `createMcpExtension()` to the `extensionFactories` of `DefaultResourceLoader`. `codemode` and `tool_search` are registered inactive: enable them through the `defaultTools` setting (`["+codemode", "+tool_search"]` keeps the other default tools), or let the MCP extension activate them: `codemode` for servers with `codemode` exposure, `tool_search` for servers with `deferred` exposure. The MCP extension connects its servers on `session_start`, so call `session.bindExtensions()`. See [Codemode and MCP](../packages/coding-agent/examples/sdk/14-codemode-mcp.ts).
See the focused examples for [models](../examples/sdk/02-custom-model.ts), [tools](../examples/sdk/05-tools.ts), [extensions](../examples/sdk/06-extensions.ts), and [full control](../examples/sdk/12-full-control.ts).
See the focused examples for [models](../packages/coding-agent/examples/sdk/02-custom-model.ts), [tools](../packages/coding-agent/examples/sdk/05-tools.ts), [extensions](../packages/coding-agent/examples/sdk/06-extensions.ts), and [full control](../packages/coding-agent/examples/sdk/12-full-control.ts).
## Examples
| Example | Purpose |
|---|---|
| [Minimal](../examples/sdk/01-minimal.ts) | Create, prompt, observe, and dispose a session |
| [Custom model](../examples/sdk/02-custom-model.ts) | Select a model and thinking level |
| [System prompt](../examples/sdk/03-custom-prompt.ts) | Replace or append to the system prompt |
| [Skills](../examples/sdk/04-skills.ts) | Discover, filter, and add skills |
| [Tools](../examples/sdk/05-tools.ts) | Select built-in tools and their working directory |
| [Extensions](../examples/sdk/06-extensions.ts) | Load file-based and inline extensions |
| [Context files](../examples/sdk/07-context-files.ts) | Add or replace project instructions |
| [Prompt templates](../examples/sdk/08-prompt-templates.ts) | Add file-style prompt templates |
| [Credentials](../examples/sdk/09-api-keys-and-oauth.ts) | Configure credential and model storage |
| [Settings](../examples/sdk/10-settings.ts) | Supply file-backed or in-memory settings |
| [Sessions](../examples/sdk/11-sessions.ts) | Control session persistence and restoration |
| [Full control](../examples/sdk/12-full-control.ts) | Replace default discovery and state services |
| [Session runtime](../examples/sdk/13-session-runtime.ts) | Replace the active session safely |
| [Codemode and MCP](../examples/sdk/14-codemode-mcp.ts) | Add the `codemode`, `tool_search`, and MCP extensions |
| [Minimal](../packages/coding-agent/examples/sdk/01-minimal.ts) | Create, prompt, observe, and dispose a session |
| [Custom model](../packages/coding-agent/examples/sdk/02-custom-model.ts) | Select a model and thinking level |
| [System prompt](../packages/coding-agent/examples/sdk/03-custom-prompt.ts) | Replace or append to the system prompt |
| [Skills](../packages/coding-agent/examples/sdk/04-skills.ts) | Discover, filter, and add skills |
| [Tools](../packages/coding-agent/examples/sdk/05-tools.ts) | Select built-in tools and their working directory |
| [Extensions](../packages/coding-agent/examples/sdk/06-extensions.ts) | Load file-based and inline extensions |
| [Context files](../packages/coding-agent/examples/sdk/07-context-files.ts) | Add or replace project instructions |
| [Prompt templates](../packages/coding-agent/examples/sdk/08-prompt-templates.ts) | Add file-style prompt templates |
| [Credentials](../packages/coding-agent/examples/sdk/09-api-keys-and-oauth.ts) | Configure credential and model storage |
| [Settings](../packages/coding-agent/examples/sdk/10-settings.ts) | Supply file-backed or in-memory settings |
| [Sessions](../packages/coding-agent/examples/sdk/11-sessions.ts) | Control session persistence and restoration |
| [Full control](../packages/coding-agent/examples/sdk/12-full-control.ts) | Replace default discovery and state services |
| [Session runtime](../packages/coding-agent/examples/sdk/13-session-runtime.ts) | Replace the active session safely |
| [Codemode and MCP](../packages/coding-agent/examples/sdk/14-codemode-mcp.ts) | Add the `codemode`, `tool_search`, and MCP extensions |
<a id="exports"></a>

View file

@ -143,7 +143,7 @@ Treat each custom component instance as belonging to one interaction. Create a n
Finish the interaction with the completion callback supplied to the component factory. It resolves the `ctx.ui.custom()` promise and disposes the component. Do not call `OverlayHandle.hide()` on an overlay created by `ctx.ui.custom()`.
See [`overlay-qa-tests.ts`](../examples/extensions/overlay-qa-tests.ts) for positioning, stacking, focus, responsive visibility, and animation behavior.
See [`overlay-qa-tests.ts`](../packages/coding-agent/examples/extensions/overlay-qa-tests.ts) for positioning, stacking, focus, responsive visibility, and animation behavior.
## Apply themes correctly
@ -185,11 +185,11 @@ Use `PI_TUI_WRITE_LOG` to capture the raw ANSI stream when diagnosing rendering
The checked extension examples cover the main patterns:
- [`preset.ts`](../examples/extensions/preset.ts) and [`tools.ts`](../examples/extensions/tools.ts) use selection and settings lists.
- [`qna.ts`](../examples/extensions/qna.ts) uses cancellable asynchronous UI.
- [`modal-editor.ts`](../examples/extensions/modal-editor.ts) replaces the editor.
- [`custom-footer.ts`](../examples/extensions/custom-footer.ts) replaces the footer.
- [`widget-placement.ts`](../examples/extensions/widget-placement.ts) places persistent content around the editor.
- [`doom-overlay/`](../examples/extensions/doom-overlay/) demonstrates a continuously rendered overlay.
- [`preset.ts`](../packages/coding-agent/examples/extensions/preset.ts) and [`tools.ts`](../packages/coding-agent/examples/extensions/tools.ts) use selection and settings lists.
- [`qna.ts`](../packages/coding-agent/examples/extensions/qna.ts) uses cancellable asynchronous UI.
- [`modal-editor.ts`](../packages/coding-agent/examples/extensions/modal-editor.ts) replaces the editor.
- [`custom-footer.ts`](../packages/coding-agent/examples/extensions/custom-footer.ts) replaces the footer.
- [`widget-placement.ts`](../packages/coding-agent/examples/extensions/widget-placement.ts) places persistent content around the editor.
- [`doom-overlay/`](../packages/coding-agent/examples/extensions/doom-overlay/) demonstrates a continuously rendered overlay.
The public exports are defined in [`packages/tui/src/index.ts`](https://github.com/earendil-works/pi/blob/main/packages/tui/src/index.ts). See [Extensions](extensions.md) for extension lifecycle, state, tools, events, and mode behavior.

View file

@ -111,4 +111,4 @@ The transcript already records the selection and every dispatched model, and `ct
Routers can call other models through `ctx.modelRegistry`, for example `ctx.modelRegistry.classify()` with a classifier model from `ctx.modelRegistry.findOfType("classifier", provider, id)`. The call adds latency before the first token of the turn.
See [`jev-router.ts`](../examples/extensions/jev-router.ts) for a complete router. It plans on a strong OpenAI Codex model chosen by the Jev classifier, lets that model make the first edit, and then switches once to a cheaper model, accepting a single prompt-cache miss. It keeps the phase as router state.
See [`jev-router.ts`](../packages/coding-agent/examples/extensions/jev-router.ts) for a complete router. It plans on a strong OpenAI Codex model chosen by the Jev classifier, lets that model make the first edit, and then switches once to a cheaper model, accepting a single prompt-cache miss. It keeps the phase as router state.

View file

@ -14,11 +14,11 @@
Pi is a minimal, extensible agent harness that you can make your own.
Adapt Pi to your workflows, not the other way around. Customize Pi with [extensions](docs/extensions.md), [skills](docs/skills.md), [prompt templates](docs/prompt-templates.md), and [themes](docs/themes.md). Bundle them as [Pi packages](docs/packages.md) and share via npm or git.
Adapt Pi to your workflows, not the other way around. Customize Pi with [extensions](../../docs/extensions.md), [skills](../../docs/skills.md), [prompt templates](../../docs/prompt-templates.md), and [themes](../../docs/themes.md). Bundle them as [Pi packages](../../docs/packages.md) and share via npm or git.
Pi ships with powerful defaults but skips features like sub-agents and plan mode. Ask Pi to build what you want, or install a package that does it your way.
Use Pi [interactively](docs/usage.md), automate it in [print or JSON mode](docs/cli.md), control it over [RPC](docs/rpc.md), or build apps with the [Pi TypeScript SDK](docs/sdk.md). See [OpenClaw](https://github.com/OpenClaw/OpenClaw) for a real-world integration.
Use Pi [interactively](../../docs/usage.md), automate it in [print or JSON mode](../../docs/cli.md), control it over [RPC](../../docs/rpc.md), or build apps with the [Pi TypeScript SDK](../../docs/sdk.md). See [OpenClaw](https://github.com/OpenClaw/OpenClaw) for a real-world integration.
## Getting started
@ -42,7 +42,7 @@ npm install -g --ignore-scripts @earendil-works/pi-coding-agent
Pi requires Node.js 22.19 or newer. The macOS, Linux, and Windows installers can install it if needed. Pi does not require dependency lifecycle scripts for a normal npm installation.
On macOS and Linux, Nix users can install the latest release with `nix profile add github:earendil-works/pi/stable`. See the [quickstart](docs/quickstart.md#1-install-pi) for updating and pinning releases.
On macOS and Linux, Nix users can install the latest release with `nix profile add github:earendil-works/pi/stable`. See the [quickstart](../../docs/quickstart.md#1-install-pi) for updating and pinning releases.
Start Pi in the directory where you want it to work:
@ -53,7 +53,7 @@ pi
For a built-in AI provider, run `/login` inside Pi to connect a subscription or API key. Then give Pi a task.
See the [documentation](docs/index.md) for full setup and usage instructions.
See the [documentation](../../docs/index.md) for full setup and usage instructions.
## Share your OSS coding agent sessions

View file

@ -31,9 +31,10 @@
"scripts": {
"clean": "shx rm -rf dist",
"build": "npm run build:unbundled && node ../../scripts/build-coding-agent-bundle.mjs",
"build:unbundled": "tsc -p tsconfig.build.json && shx chmod +x dist/cli.js dist/rpc-entry.js && npm run copy-assets",
"build:unbundled": "tsc -p tsconfig.build.json && shx chmod +x dist/cli.js dist/rpc-entry.js && npm run copy-assets && npm run copy-docs",
"build:binary": "npm --prefix ../tui run build && npm --prefix ../telemetry run build && npm --prefix ../ai run build && npm --prefix ../agent run build && npm run build && bun build --compile --no-compile-autoload-bunfig --no-compile-autoload-dotenv ./dist/bun/cli.js ./src/utils/image-resize-worker.ts ./src/extensions/codemode/worker.ts --outfile dist/pi && npm run copy-binary-assets",
"copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/modes/interactive/assets && shx cp src/modes/interactive/assets/*.png dist/modes/interactive/assets/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/",
"copy-docs": "node scripts/copy-docs.mjs",
"copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/assets && shx cp src/modes/interactive/assets/*.png dist/assets/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp -r docs dist/ && shx cp -r examples dist/ && shx cp ../../node_modules/@silvia-odwyer/photon-node/photon_rs_bg.wasm dist/",
"test": "vitest --run",
"generate:schemas": "node --import ./src/experimental/source-resolver.ts scripts/generate-schemas.ts",

View file

@ -0,0 +1,43 @@
#!/usr/bin/env node
// Copies the repository documentation (docs/) into this package (packages/coding-agent/docs/, gitignored)
// so the npm package and the release binaries ship it. npm can only pack files inside the package, and
// every release path packs after the build, so this runs as part of the build.
//
// The docs link to coding-agent files through the repository layout, e.g.
// ../packages/coding-agent/examples/sdk/. The package has the same files at ../examples/sdk/ relative to
// its docs/, so links that point into this package are rewritten to package-relative paths. Links to
// other repository files are left unchanged.
import { cpSync, globSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { dirname, join, relative, resolve, sep } from "node:path";
import { fileURLToPath } from "node:url";
const packageDir = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const sourceDir = resolve(packageDir, "../../docs");
const targetDir = join(packageDir, "docs");
// Relative targets of inline Markdown links and HTML src attributes, without query or fragment.
const RELATIVE_LINK_RE = /(\]\(|src=")(\.\.?\/[^)"\s?#]*)/g;
function toPackageRelativeLink(sourceFile, targetFile, link) {
const linkedPath = resolve(dirname(sourceFile), link);
const packageRelativePath = relative(packageDir, linkedPath);
if (packageRelativePath.startsWith(`..${sep}`) || packageRelativePath === "..") return link;
const rewritten = relative(dirname(targetFile), linkedPath).split(sep).join("/");
return link.endsWith("/") ? `${rewritten}/` : rewritten;
}
rmSync(targetDir, { recursive: true, force: true });
cpSync(sourceDir, targetDir, { recursive: true });
for (const path of globSync("**/*.md", { cwd: sourceDir })) {
const sourceFile = join(sourceDir, path);
const targetFile = join(targetDir, path);
const markdown = readFileSync(sourceFile, "utf8");
const rewritten = markdown.replace(
RELATIVE_LINK_RE,
(_match, prefix, link) => `${prefix}${toPackageRelativeLink(sourceFile, targetFile, link)}`,
);
if (rewritten !== markdown) writeFileSync(targetFile, rewritten);
}

View file

@ -445,9 +445,17 @@ export function getReadmePath(): string {
return resolve(join(getPackageDir(), "README.md"));
}
/** Get path to docs directory */
/**
* Get path to docs directory
* - For source checkouts: the repository docs/ (the source of truth)
* - For npm installs and the Bun binary: docs/ in the package, copied there by scripts/copy-docs.mjs
*/
export function getDocsPath(): string {
return resolve(join(getPackageDir(), "docs"));
const packageDir = getPackageDir();
if (!isBunBinary && existsSync(join(packageDir, "src"))) {
return resolve(packageDir, "..", "..", "docs");
}
return resolve(join(packageDir, "docs"));
}
/** Get path to examples directory */

View file

@ -9,7 +9,7 @@
import { basename, dirname, isAbsolute, relative, resolve as resolvePath, sep } from "node:path";
import type { ImageContent, TextContent } from "@earendil-works/pi-ai";
import { Text } from "@earendil-works/pi-tui";
import { getReadmePath } from "../../../config.ts";
import { getDocsPath, getReadmePath } from "../../../config.ts";
import { keyHint, keyText } from "../../../modes/interactive/components/keybinding-hints.ts";
import { getLanguageFromPath, highlightCode, type Theme } from "../../../modes/interactive/theme/theme.ts";
import { formatPathRelativeToCwdOrAbsolute } from "../../../utils/paths.ts";
@ -43,9 +43,8 @@ function trimTrailingEmptyLines(lines: string[]): string[] {
function toPosixPath(filePath: string): string {
return filePath.split(sep).join("/");
}
function getPiDocsClassification(absolutePath: string): CompactReadClassification | undefined {
const packageRoot = dirname(getReadmePath());
const relativePath = relative(resolvePath(packageRoot), resolvePath(absolutePath));
function getContainedRelativePath(directory: string, absolutePath: string): string | undefined {
const relativePath = relative(resolvePath(directory), resolvePath(absolutePath));
if (
relativePath === "" ||
relativePath === ".." ||
@ -54,9 +53,17 @@ function getPiDocsClassification(absolutePath: string): CompactReadClassificatio
) {
return undefined;
}
return toPosixPath(relativePath);
}
function getPiDocsClassification(absolutePath: string): CompactReadClassification | undefined {
// Docs live outside the package root in source checkouts, so check them separately.
const docsPath = getContainedRelativePath(getDocsPath(), absolutePath);
if (docsPath) {
return { kind: "docs", label: `docs/${docsPath}` };
}
const label = toPosixPath(relativePath);
if (label === "README.md" || label.startsWith("docs/") || label.startsWith("examples/")) {
const label = getContainedRelativePath(dirname(getReadmePath()), absolutePath);
if (label === "README.md" || label?.startsWith("examples/")) {
return { kind: "docs", label };
}
return undefined;

View file

@ -11,7 +11,7 @@ import { stripBom } from "./utils/text.ts";
const MIGRATION_GUIDE_URL =
"https://github.com/earendil-works/pi/blob/main/packages/coding-agent/CHANGELOG.md#extensions-migration";
const EXTENSIONS_DOC_URL = "https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/extensions.md";
const EXTENSIONS_DOC_URL = "https://github.com/earendil-works/pi/blob/main/docs/extensions.md";
/**
* Migrate legacy oauth.json and settings.json apiKeys to auth.json.

View file

@ -12,7 +12,7 @@ interface NavigationGroup {
items: NavigationItem[];
}
const docsRoot = resolve(import.meta.dirname, "../docs");
const docsRoot = resolve(import.meta.dirname, "../../../docs");
function requireObject(value: unknown, location: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {

View file

@ -3,7 +3,7 @@ import { resetCapabilitiesCache, setCapabilities, Text, type TUI, type TuiMouseE
import { Type } from "typebox";
import { afterEach, beforeAll, describe, expect, test, vi } from "vitest";
import { getReadmePath } from "../src/config.ts";
import { getDocsPath, getReadmePath } from "../src/config.ts";
import type { ToolDefinition } from "../src/core/extensions/types.ts";
import { type BashOperations, createBashToolDefinition } from "../src/core/tools/bash.ts";
import { createReadTool, createReadToolDefinition } from "../src/core/tools/read.ts";
@ -672,6 +672,14 @@ describe("ToolExecutionComponent parity", () => {
hidden: "Hidden docs content",
absent: undefined,
},
{
title: "Pi documentation page",
path: join(getDocsPath(), "extensions.md"),
content: "Hidden docs page content",
compact: "read docs docs/extensions.md",
hidden: "Hidden docs page content",
absent: undefined,
},
] as const) {
test(`renders ${scenario.title} read results compactly until expanded`, () => {
const component = new ToolExecutionComponent(

View file

@ -30,7 +30,7 @@ const submitAudit = defineTool({
});
const repositoryRoot = resolve(import.meta.dirname, "../../..");
const docsRoot = resolve(repositoryRoot, "packages/coding-agent/docs");
const docsRoot = resolve(repositoryRoot, "docs");
const pages = globSync("**/*.md", { cwd: docsRoot })
.map((path) => ({ path: path.replaceAll("\\", "/") }))
.sort((left, right) => left.path.localeCompare(right.path));