@counterposition/skill-pi 0.1.0
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.
- package/LICENSE.md +675 -0
- package/README.md +17 -0
- package/package.json +42 -0
- package/skills/pi/LICENSE.md +675 -0
- package/skills/pi/README.md +38 -0
- package/skills/pi/SKILL.md +170 -0
- package/skills/pi/evals/evals.json +50 -0
- package/skills/pi/references/extensions.md +343 -0
- package/skills/pi/references/packages.md +97 -0
- package/skills/pi/references/patterns.md +314 -0
- package/skills/pi/references/providers.md +212 -0
- package/skills/pi/references/sdk.md +177 -0
- package/skills/pi/references/settings.md +170 -0
- package/skills/pi/references/skills.md +111 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Pi Skill
|
|
2
|
+
|
|
3
|
+
Reference skill for the Pi coding agent.
|
|
4
|
+
|
|
5
|
+
- Use when working on Pi extensions, skills, settings, packages, SDK usage, providers, sessions, or compaction.
|
|
6
|
+
- Good trigger terms: `.pi/`, `SKILL.md`, `models.json`, `pi-ai`, `pi-tui`, `pi-agent-core`, `pi-coding-agent`.
|
|
7
|
+
- Not for Raspberry Pi hardware or the math constant.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# multi-skill repo
|
|
13
|
+
npx skills add counterposition/pi --skill pi
|
|
14
|
+
|
|
15
|
+
# local checkout
|
|
16
|
+
npx skills add /absolute/path/to/pi --skill pi
|
|
17
|
+
|
|
18
|
+
# pi install from npm
|
|
19
|
+
pi install npm:@counterposition/skill-pi
|
|
20
|
+
|
|
21
|
+
# pi install from local package
|
|
22
|
+
pi install ./packages/skill-pi
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Covers
|
|
26
|
+
|
|
27
|
+
- Pi architecture and package layout
|
|
28
|
+
- Skill authoring and discovery
|
|
29
|
+
- Extensions, tools, and UI hooks
|
|
30
|
+
- Settings, models, providers, and packages
|
|
31
|
+
- Sessions, forks, and compaction
|
|
32
|
+
|
|
33
|
+
## Files
|
|
34
|
+
|
|
35
|
+
- `SKILL.md` - trigger description and routing
|
|
36
|
+
- `LICENSE.md` - bundled GPLv3 license text
|
|
37
|
+
- `references/` - task-specific Pi docs
|
|
38
|
+
- `evals/evals.json` - example eval cases
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pi
|
|
3
|
+
description: "Provides Pi-specific guidance for extensions, skills, `.pi/` config, `settings.json`, `models.json`, packages, providers, themes, SDK/RPC, sessions, and compaction in Pi, the coding agent. Use when the user mentions Pi or Pi-specific terms such as `.pi/`, `SKILL.md`, `createAgentSession()`, `thinkingLevel`, `session_before_compact`, `pi-ai`, `pi-tui`, `pi-agent-core`, or `pi-coding-agent`. Not for Raspberry Pi hardware, the math constant, or unrelated generic tooling."
|
|
4
|
+
license: GPLv3
|
|
5
|
+
compatibility: "Pi-specific guidance for agents that support Agent Skills"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Pi Coding Agent
|
|
9
|
+
|
|
10
|
+
Pi is a minimal, extensible terminal coding agent. It ships four default tools (read, write, edit, bash) and four execution modes (interactive TUI, print/JSON, RPC, SDK). Everything else — sub-agents, plan mode, permission popups, MCP — is added through extensions.
|
|
11
|
+
|
|
12
|
+
Pi's philosophy: **adapt Pi to your workflows, not the other way around**. Users extend it via TypeScript extensions, skills, prompt templates, themes, and shareable Pi packages.
|
|
13
|
+
|
|
14
|
+
## Architecture at a Glance
|
|
15
|
+
|
|
16
|
+
Pi is a monorepo with four core packages:
|
|
17
|
+
|
|
18
|
+
| Package | npm | Purpose |
|
|
19
|
+
|---------|-----|---------|
|
|
20
|
+
| `pi-ai` | `@mariozechner/pi-ai` | Unified LLM API across 20+ providers |
|
|
21
|
+
| `pi-agent-core` | `@mariozechner/pi-agent-core` | Agent loop, tool execution, state management |
|
|
22
|
+
| `pi-tui` | `@mariozechner/pi-tui` | Terminal UI component library |
|
|
23
|
+
| `pi-coding-agent` | `@mariozechner/pi-coding-agent` | CLI, extensions, skills, sessions, settings |
|
|
24
|
+
|
|
25
|
+
**Source:** [github.com/badlogic/pi-mono](https://github.com/badlogic/pi-mono)
|
|
26
|
+
|
|
27
|
+
## File System Layout
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
~/.pi/agent/ # Global config directory (PI_CODING_AGENT_DIR overrides)
|
|
31
|
+
├── settings.json # Global settings
|
|
32
|
+
├── auth.json # Credentials (0600 perms)
|
|
33
|
+
├── models.json # Custom provider/model definitions
|
|
34
|
+
├── keybindings.json # Keyboard shortcuts
|
|
35
|
+
├── presets.json # Named configurations (via preset extension)
|
|
36
|
+
├── extensions/ # Global extensions (auto-discovered)
|
|
37
|
+
├── skills/ # Global skills (auto-discovered)
|
|
38
|
+
├── prompts/ # Global prompt templates (auto-discovered)
|
|
39
|
+
├── themes/ # Custom themes
|
|
40
|
+
├── sessions/ # JSONL session files
|
|
41
|
+
└── bin/ # Tool binaries (fd, ripgrep)
|
|
42
|
+
|
|
43
|
+
<project>/
|
|
44
|
+
├── .pi/ # Project-local config (overrides global)
|
|
45
|
+
│ ├── settings.json # Project settings (merged over global)
|
|
46
|
+
│ ├── extensions/ # Project extensions (auto-discovered)
|
|
47
|
+
│ ├── skills/ # Project skills
|
|
48
|
+
│ ├── prompts/ # Project prompt templates
|
|
49
|
+
│ ├── themes/ # Project themes
|
|
50
|
+
│ └── agents/ # Agent definitions (for subagent extension)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Key Concepts
|
|
54
|
+
|
|
55
|
+
### Extensions
|
|
56
|
+
|
|
57
|
+
TypeScript modules that hook into Pi's lifecycle. They can register tools, intercept tool calls, add commands, build custom UIs, and persist state. Extensions have **full system access** — they run with the user's permissions.
|
|
58
|
+
|
|
59
|
+
Read `references/extensions.md` for the complete API, lifecycle events, tool registration, UI methods, and working examples.
|
|
60
|
+
|
|
61
|
+
### Skills
|
|
62
|
+
|
|
63
|
+
Markdown-based capability packages (`SKILL.md` with frontmatter). Pi loads skill names and descriptions into the system prompt; the full skill body is read on demand. Skills follow the Agent Skills standard.
|
|
64
|
+
|
|
65
|
+
Read `references/skills.md` for the authoring guide, frontmatter schema, and directory conventions.
|
|
66
|
+
|
|
67
|
+
### Settings
|
|
68
|
+
|
|
69
|
+
Hierarchical JSON configuration: project `.pi/settings.json` merges over global `~/.pi/agent/settings.json`. Controls model, thinking level, compaction, retry, shell, transport, and resource loading.
|
|
70
|
+
|
|
71
|
+
Read `references/settings.md` for every setting with its type, default, and effect.
|
|
72
|
+
|
|
73
|
+
### Packages
|
|
74
|
+
|
|
75
|
+
Bundles of extensions, skills, prompts, and themes distributed via npm, git, or local paths. Installed with `pi install`.
|
|
76
|
+
|
|
77
|
+
Read `references/packages.md` for the package structure, `pi` manifest field, installation, and publishing.
|
|
78
|
+
|
|
79
|
+
### SDK
|
|
80
|
+
|
|
81
|
+
Programmatic embedding via `createAgentSession()`. Gives full control over models, tools, extensions, sessions, and event streaming.
|
|
82
|
+
|
|
83
|
+
Read `references/sdk.md` for factory options, event types, and usage patterns.
|
|
84
|
+
|
|
85
|
+
### Custom Providers & Models
|
|
86
|
+
|
|
87
|
+
Add any OpenAI-compatible, Anthropic, Google, or custom LLM endpoint via `models.json` or extension-based `registerProvider()`.
|
|
88
|
+
|
|
89
|
+
Read `references/providers.md` for provider configuration, auth methods, and the model schema.
|
|
90
|
+
|
|
91
|
+
### Common Patterns
|
|
92
|
+
|
|
93
|
+
Practical recipes for permission gates, presets, sub-agents, git checkpoints, custom compaction, TUI components, and more.
|
|
94
|
+
|
|
95
|
+
Read `references/patterns.md` for copy-paste-ready examples.
|
|
96
|
+
|
|
97
|
+
## Sessions & Compaction
|
|
98
|
+
|
|
99
|
+
Sessions are append-only JSONL files with a tree structure (entries linked by `id`/`parentId`). This allows branching without modifying history. Key operations:
|
|
100
|
+
|
|
101
|
+
- **`/tree`** — Navigate the session tree, jump to any point
|
|
102
|
+
- **`/compact`** — Manually trigger context compaction
|
|
103
|
+
- **`/fork`** — Branch from a specific point
|
|
104
|
+
- **Auto-compaction** — Triggers when `contextTokens > contextWindow - reserveTokens`
|
|
105
|
+
|
|
106
|
+
Compaction walks backward from the newest message, keeping `keepRecentTokens` (default 20,000) of recent conversation, and summarizes everything older. Extensions can intercept compaction via `session_before_compact`.
|
|
107
|
+
|
|
108
|
+
## Built-in Tools
|
|
109
|
+
|
|
110
|
+
| Tool | Description |
|
|
111
|
+
|------|-------------|
|
|
112
|
+
| `read` | Read files (with offset/limit support, images, PDFs) |
|
|
113
|
+
| `write` | Create or overwrite files |
|
|
114
|
+
| `edit` | Line-based file editing with diffs |
|
|
115
|
+
| `bash` | Execute shell commands with streaming output |
|
|
116
|
+
| `grep` | Search file contents by pattern |
|
|
117
|
+
| `find` | Find files by glob pattern |
|
|
118
|
+
| `ls` | List directory contents |
|
|
119
|
+
|
|
120
|
+
**Tool presets:** `CODING_TOOLS` = [read, bash, edit, write], `READ_ONLY_TOOLS` = [read, grep, find, ls], `ALL_TOOLS` = all seven.
|
|
121
|
+
|
|
122
|
+
## Execution Modes
|
|
123
|
+
|
|
124
|
+
| Mode | Start with | Use case |
|
|
125
|
+
|------|-----------|----------|
|
|
126
|
+
| Interactive | `pi` | Full TUI with editor, streaming, keybindings |
|
|
127
|
+
| Print | `pi -p "prompt"` | Single-shot text output |
|
|
128
|
+
| JSON | `pi --mode json -p "prompt"` | Newline-delimited JSON events |
|
|
129
|
+
| RPC | `pi --mode rpc` | JSON-RPC over stdin/stdout for IDE integration |
|
|
130
|
+
| SDK | `createAgentSession()` | Embed in Node.js applications |
|
|
131
|
+
|
|
132
|
+
## Quick Reference: CLI Flags
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
pi [options] [message]
|
|
136
|
+
|
|
137
|
+
--model, -m <model> Model (e.g., "claude-sonnet-4-20250514")
|
|
138
|
+
--provider <name> Provider name
|
|
139
|
+
--api-key <key> API key override
|
|
140
|
+
--tools <list> Comma-separated tool names
|
|
141
|
+
--extensions, -e <path> Load extension from path
|
|
142
|
+
--skills <path> Load skill from path
|
|
143
|
+
--system-prompt <text> Override system prompt
|
|
144
|
+
--thinking-level <level> off | minimal | low | medium | high | xhigh
|
|
145
|
+
--new Start new session
|
|
146
|
+
--resume <id> Resume session by ID
|
|
147
|
+
--fork <id> Fork from session
|
|
148
|
+
--continue-recent Continue most recent session
|
|
149
|
+
--no-session Don't persist session
|
|
150
|
+
--mode <mode> json | rpc
|
|
151
|
+
-p Print mode (non-interactive)
|
|
152
|
+
install <source> Install package (npm:pkg, git:url, path)
|
|
153
|
+
remove <pkg> Remove package
|
|
154
|
+
update Update packages
|
|
155
|
+
list List installed packages
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## How to Use This Skill
|
|
159
|
+
|
|
160
|
+
When working with Pi, read the appropriate reference file for the task at hand:
|
|
161
|
+
|
|
162
|
+
| Task | Reference |
|
|
163
|
+
|------|-----------|
|
|
164
|
+
| Writing an extension (tools, commands, events, UI) | `references/extensions.md` |
|
|
165
|
+
| Creating a skill (SKILL.md authoring) | `references/skills.md` |
|
|
166
|
+
| Configuring Pi (settings.json options) | `references/settings.md` |
|
|
167
|
+
| Building a shareable package | `references/packages.md` |
|
|
168
|
+
| Embedding Pi programmatically | `references/sdk.md` |
|
|
169
|
+
| Adding LLM providers or models | `references/providers.md` |
|
|
170
|
+
| Looking for a recipe or pattern | `references/patterns.md` |
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "pi",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "Write a Pi extension that blocks any bash command containing `curl` unless the user confirms, and logs all blocked commands to a file at ~/.pi/agent/blocked-commands.log",
|
|
7
|
+
"expected_output": "A TypeScript extension file that hooks into tool_call events, checks for curl in bash commands, prompts via ctx.ui.select, and appends blocked commands to a log file. Should check ctx.hasUI and handle non-interactive mode.",
|
|
8
|
+
"files": [],
|
|
9
|
+
"assertions": [
|
|
10
|
+
"Uses correct ExtensionAPI import from @mariozechner/pi-coding-agent",
|
|
11
|
+
"Hooks into tool_call event (the correct event for intercepting tool calls)",
|
|
12
|
+
"Checks event.toolName === 'bash' before inspecting command",
|
|
13
|
+
"Tests for curl in the command string",
|
|
14
|
+
"Checks ctx.hasUI before calling interactive UI methods",
|
|
15
|
+
"Uses ctx.ui.select or ctx.ui.confirm for user confirmation",
|
|
16
|
+
"Returns { block: true, reason: '...' } to prevent execution",
|
|
17
|
+
"Logs blocked commands to ~/.pi/agent/blocked-commands.log",
|
|
18
|
+
"Uses correct export default function (pi: ExtensionAPI) signature"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": 2,
|
|
23
|
+
"prompt": "I want to set up Pi to use Ollama with llama3.2 locally, with auto-compaction disabled and a custom session directory at ~/pi-sessions. Walk me through the configuration.",
|
|
24
|
+
"expected_output": "Instructions covering models.json with Ollama provider config (baseUrl localhost:11434/v1, api openai-completions, dummy apiKey, model id llama3.2), settings.json with compaction.enabled=false and sessionDir pointing to ~/pi-sessions.",
|
|
25
|
+
"files": [],
|
|
26
|
+
"assertions": [
|
|
27
|
+
"Provides models.json with Ollama baseUrl containing localhost:11434",
|
|
28
|
+
"Uses api type openai-completions (correct for Ollama)",
|
|
29
|
+
"Includes model entry with id llama3.2",
|
|
30
|
+
"Provides settings.json with compaction.enabled set to false",
|
|
31
|
+
"Sets sessionDir to ~/pi-sessions",
|
|
32
|
+
"Correct file paths: ~/.pi/agent/models.json and ~/.pi/agent/settings.json"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": 3,
|
|
37
|
+
"prompt": "Create a Pi skill that helps with database migrations — it should know about Prisma, Drizzle, and raw SQL approaches. Give me the full SKILL.md and any reference files.",
|
|
38
|
+
"expected_output": "A SKILL.md with proper frontmatter (name, description), a main body routing to reference files, and separate reference docs for Prisma, Drizzle, and raw SQL migration patterns.",
|
|
39
|
+
"files": [],
|
|
40
|
+
"assertions": [
|
|
41
|
+
"SKILL.md has valid YAML frontmatter with name and description fields",
|
|
42
|
+
"Name follows Pi convention: lowercase, hyphens only, max 64 chars",
|
|
43
|
+
"Description mentions trigger contexts (migrations, Prisma, Drizzle, SQL)",
|
|
44
|
+
"Includes separate reference files for Prisma, Drizzle, and raw SQL",
|
|
45
|
+
"SKILL.md uses relative paths to reference files",
|
|
46
|
+
"Body routes to the correct reference doc based on the user's framework choice"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# Extensions
|
|
2
|
+
|
|
3
|
+
Extensions are TypeScript modules that customize Pi's behavior. They have **full system permissions** — they can read/write files, run commands, make network requests, and access anything the user can.
|
|
4
|
+
|
|
5
|
+
## Placement & Discovery
|
|
6
|
+
|
|
7
|
+
| Location | Scope | Auto-discovered |
|
|
8
|
+
|----------|-------|-----------------|
|
|
9
|
+
| `~/.pi/agent/extensions/*.ts` | Global (all projects) | Yes |
|
|
10
|
+
| `.pi/extensions/*.ts` | Project-local | Yes |
|
|
11
|
+
| `settings.json` → `extensions` | Configured | Yes |
|
|
12
|
+
| `pi -e ./path.ts` | CLI one-off | No |
|
|
13
|
+
|
|
14
|
+
Subdirectories with an `index.ts` or a `package.json` containing a `"pi"` field are also discovered. Reload with `/reload`.
|
|
15
|
+
|
|
16
|
+
## Basic Structure
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
20
|
+
|
|
21
|
+
export default function (pi: ExtensionAPI) {
|
|
22
|
+
// Subscribe to lifecycle events
|
|
23
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
24
|
+
ctx.ui.notify("Extension loaded!", "info");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Register a tool the LLM can call
|
|
28
|
+
pi.registerTool({
|
|
29
|
+
name: "greet",
|
|
30
|
+
label: "Greet",
|
|
31
|
+
description: "Greet someone by name",
|
|
32
|
+
parameters: Type.Object({
|
|
33
|
+
name: Type.String({ description: "Name to greet" }),
|
|
34
|
+
}),
|
|
35
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
36
|
+
const { name } = params as { name: string };
|
|
37
|
+
return {
|
|
38
|
+
content: [{ type: "text", text: `Hello, ${name}!` }],
|
|
39
|
+
details: { greeted: name },
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Register a slash command
|
|
45
|
+
pi.registerCommand("mycommand", {
|
|
46
|
+
description: "Do something",
|
|
47
|
+
async handler(args, ctx) {
|
|
48
|
+
ctx.ui.notify(`You said: ${args}`, "info");
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Import note:** Use `Type` from `@mariozechner/pi-ai` (re-exports TypeBox) and `StringEnum` from the same package for string enums (required for Google API compatibility — do not use `Type.Union` with `Type.Literal` for string enums).
|
|
55
|
+
|
|
56
|
+
## Lifecycle Events
|
|
57
|
+
|
|
58
|
+
### Session Events
|
|
59
|
+
|
|
60
|
+
| Event | When | Payload |
|
|
61
|
+
|-------|------|---------|
|
|
62
|
+
| `session_start` | Session begins | `{ sessionId }` |
|
|
63
|
+
| `session_switch` | User switches session | `{ sessionId }` |
|
|
64
|
+
| `session_fork` | Session forked | `{ entryId }` |
|
|
65
|
+
| `session_before_fork` | Before fork executes | `{ entryId }` |
|
|
66
|
+
| `session_shutdown` | Session closing | `{}` |
|
|
67
|
+
| `session_tree` | After `/tree` navigation | `{ entryId }` |
|
|
68
|
+
| `session_before_tree` | Before `/tree` navigation | `{ entryId, messages }` |
|
|
69
|
+
| `session_before_compact` | Before compaction | `{ messages, settings, ... }` |
|
|
70
|
+
| `session_compact` | After compaction | `{ summary }` |
|
|
71
|
+
| `resources_discover` | Resource discovery phase | `{ extensions, skills, prompts, themes }` |
|
|
72
|
+
|
|
73
|
+
### Agent Events
|
|
74
|
+
|
|
75
|
+
| Event | When | Payload |
|
|
76
|
+
|-------|------|---------|
|
|
77
|
+
| `before_agent_start` | Before LLM call (modify system prompt) | `{ systemPrompt }` |
|
|
78
|
+
| `agent_start` | Agent begins processing | `{}` |
|
|
79
|
+
| `agent_end` | Agent finishes | `{}` |
|
|
80
|
+
| `turn_start` | New LLM turn begins | `{}` |
|
|
81
|
+
| `turn_end` | LLM turn finishes | `{}` |
|
|
82
|
+
|
|
83
|
+
### Tool Events
|
|
84
|
+
|
|
85
|
+
| Event | When | Payload |
|
|
86
|
+
|-------|------|---------|
|
|
87
|
+
| `tool_call` | Before tool executes | `{ toolName, input }` — return `{ block, reason }` to prevent |
|
|
88
|
+
| `tool_result` | After tool executes | `{ toolName, result }` |
|
|
89
|
+
| `tool_execution_start` | Tool execution begins | `{ toolCallId, toolName }` |
|
|
90
|
+
| `tool_execution_end` | Tool execution ends | `{ toolCallId, toolName }` |
|
|
91
|
+
|
|
92
|
+
### Other Events
|
|
93
|
+
|
|
94
|
+
| Event | When | Payload |
|
|
95
|
+
|-------|------|---------|
|
|
96
|
+
| `input` | User submits input | `{ text }` — return modified text or `{ block }` |
|
|
97
|
+
| `model_select` | Model selector opened | `{ models }` — return filtered list |
|
|
98
|
+
|
|
99
|
+
## Event Handler Signature
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
pi.on("event_name", async (event, ctx) => {
|
|
103
|
+
// event: event-specific payload
|
|
104
|
+
// ctx: ExtensionContext
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### ExtensionContext
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
interface ExtensionContext {
|
|
112
|
+
ui: ExtensionUIContext; // UI interaction methods
|
|
113
|
+
cwd: string; // Working directory
|
|
114
|
+
session: AgentSession; // Session access
|
|
115
|
+
sessionManager: SessionManager; // JSONL session persistence
|
|
116
|
+
modelRegistry: ModelRegistry; // Available models
|
|
117
|
+
hasUI: boolean; // Whether interactive mode is available
|
|
118
|
+
isIdle(): boolean; // Whether the agent is idle
|
|
119
|
+
abort(): void; // Stop current generation
|
|
120
|
+
getContextUsage(): ContextUsage; // Token usage info
|
|
121
|
+
getSystemPrompt(): string; // Current system prompt
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Tool Registration
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
pi.registerTool({
|
|
129
|
+
name: "tool_name", // Unique identifier
|
|
130
|
+
label: "Tool Name", // Display name
|
|
131
|
+
description: "What this tool does", // Shown to LLM
|
|
132
|
+
parameters: Type.Object({ // TypeBox schema
|
|
133
|
+
arg1: Type.String({ description: "..." }),
|
|
134
|
+
arg2: Type.Optional(Type.Number()),
|
|
135
|
+
}),
|
|
136
|
+
|
|
137
|
+
// Execute the tool (called by LLM)
|
|
138
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
139
|
+
// params: parsed and validated parameters
|
|
140
|
+
// signal: AbortSignal for cancellation — check signal?.aborted
|
|
141
|
+
// onUpdate: callback for streaming partial results
|
|
142
|
+
// ctx: ExtensionContext
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
content: [{ type: "text", text: "result" }],
|
|
146
|
+
details: { anyMetadata: true }, // Stored in session, available on branch restore
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
// Optional: custom TUI rendering for tool calls
|
|
151
|
+
renderCall(args, theme, context) {
|
|
152
|
+
return new Text(theme.fg("accent", `tool ${args.arg1}`), 0, 0);
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
// Optional: custom TUI rendering for tool results
|
|
156
|
+
renderResult(result, { expanded }, theme, context) {
|
|
157
|
+
return new Text(result.content[0].text, 0, 0);
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Critical rules for tools:**
|
|
163
|
+
|
|
164
|
+
1. **Truncate output** — Default limits are 50KB or 2000 lines. Use `truncateHead()` or `truncateTail()` from the tools module.
|
|
165
|
+
2. **Handle cancellation** — Check `signal?.aborted` in long-running operations.
|
|
166
|
+
3. **Throw on error** — Throwing signals failure to the LLM. Returning always means success.
|
|
167
|
+
4. **Use `withFileMutationQueue()`** — If your tool modifies files, wrap the operation to participate in the mutation queue.
|
|
168
|
+
5. **Store state in `details`** — Tool result `details` persist in session history and survive branching. Reconstruct state from session entries on `session_start`.
|
|
169
|
+
|
|
170
|
+
## UI Methods
|
|
171
|
+
|
|
172
|
+
All UI methods are on `ctx.ui`. **Always check `ctx.hasUI` first** — non-interactive modes (print, JSON, RPC) may not support UI.
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// Dialogs (blocking — waits for user response)
|
|
176
|
+
const choice = await ctx.ui.select("Pick one:", ["Option A", "Option B"]);
|
|
177
|
+
const confirmed = await ctx.ui.confirm("Are you sure?", "Details here");
|
|
178
|
+
const text = await ctx.ui.input("Enter a value:", "default");
|
|
179
|
+
const edited = await ctx.ui.editor("Edit this text", initialContent);
|
|
180
|
+
|
|
181
|
+
// Notifications (non-blocking)
|
|
182
|
+
ctx.ui.notify("Something happened", "info"); // "info" | "warning" | "error"
|
|
183
|
+
|
|
184
|
+
// Status indicators
|
|
185
|
+
ctx.ui.setStatus("Processing..."); // Footer status text
|
|
186
|
+
ctx.ui.setWidget("above", component); // Widget above/below editor
|
|
187
|
+
ctx.ui.setFooter(component); // Custom footer component
|
|
188
|
+
|
|
189
|
+
// Editor control
|
|
190
|
+
ctx.ui.setEditorText("prefilled text");
|
|
191
|
+
ctx.ui.pasteToEditor("appended text");
|
|
192
|
+
|
|
193
|
+
// Custom components (advanced)
|
|
194
|
+
const handle = ctx.ui.custom(component, { overlay: true });
|
|
195
|
+
handle.requestRender();
|
|
196
|
+
handle.close();
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Command Registration
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
pi.registerCommand("commandname", {
|
|
203
|
+
description: "What this command does", // Shown in autocomplete
|
|
204
|
+
args: "optional args description",
|
|
205
|
+
|
|
206
|
+
// Argument autocompletion
|
|
207
|
+
complete(partial) {
|
|
208
|
+
return ["suggestion1", "suggestion2"].filter(s => s.startsWith(partial));
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
// Handler receives ExtensionCommandContext (superset of ExtensionContext)
|
|
212
|
+
async handler(args, ctx) {
|
|
213
|
+
await ctx.waitForIdle(); // Wait for agent to finish
|
|
214
|
+
ctx.newSession(); // Create new session
|
|
215
|
+
ctx.fork(entryId); // Fork from entry
|
|
216
|
+
ctx.navigateTree(); // Open tree navigator
|
|
217
|
+
ctx.reload(); // Reload extensions
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Keybinding Registration
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
pi.registerKeybinding({
|
|
226
|
+
key: "ctrl+shift+k",
|
|
227
|
+
description: "Do something",
|
|
228
|
+
handler: async (ctx) => { /* ... */ },
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## CLI Flag Registration
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
pi.registerFlag({
|
|
236
|
+
name: "--my-flag",
|
|
237
|
+
description: "Enable something",
|
|
238
|
+
handler: (value) => { /* value is the flag argument */ },
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## State Persistence
|
|
243
|
+
|
|
244
|
+
Extensions persist state via custom session entries:
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
// Write state
|
|
248
|
+
pi.appendEntry("my-extension-state", { key: "value" });
|
|
249
|
+
|
|
250
|
+
// Read state on session restore
|
|
251
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
252
|
+
const branch = ctx.sessionManager.getBranch();
|
|
253
|
+
for (const entry of branch.reverse()) {
|
|
254
|
+
if (entry.type === "custom" && entry.name === "my-extension-state") {
|
|
255
|
+
const state = entry.data;
|
|
256
|
+
// Restore from state
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Provider Registration
|
|
264
|
+
|
|
265
|
+
Extensions can register custom LLM providers:
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
pi.registerProvider("my-provider", {
|
|
269
|
+
baseUrl: "https://api.example.com",
|
|
270
|
+
api: "openai-completions",
|
|
271
|
+
apiKey: "sk-...",
|
|
272
|
+
models: [
|
|
273
|
+
{ id: "my-model", name: "My Model", contextWindow: 128000, maxTokens: 4096 },
|
|
274
|
+
],
|
|
275
|
+
});
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
For full custom streaming, implement `streamSimple` on the provider config. See `references/providers.md`.
|
|
279
|
+
|
|
280
|
+
## Bash Execution
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
const { stdout, stderr, exitCode } = await pi.exec("git", ["status"]);
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Practical Examples
|
|
287
|
+
|
|
288
|
+
### Permission Gate
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
pi.on("tool_call", async (event, ctx) => {
|
|
292
|
+
if (event.toolName !== "bash") return;
|
|
293
|
+
const cmd = event.input.command as string;
|
|
294
|
+
if (/\brm\s+(-rf?|--recursive)/i.test(cmd)) {
|
|
295
|
+
if (!ctx.hasUI) return { block: true, reason: "Dangerous command blocked" };
|
|
296
|
+
const ok = await ctx.ui.select(`⚠️ Dangerous: ${cmd}\nAllow?`, ["Yes", "No"]);
|
|
297
|
+
if (ok !== "Yes") return { block: true, reason: "Blocked by user" };
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### System Prompt Injection
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
pi.on("before_agent_start", async (event, _ctx) => {
|
|
306
|
+
event.systemPrompt += "\n\nAdditional instructions here.";
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Git Checkpoint on Every Turn
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
pi.on("turn_start", async () => {
|
|
314
|
+
const { stdout } = await pi.exec("git", ["stash", "create"]);
|
|
315
|
+
if (stdout.trim()) checkpoints.set(currentEntryId, stdout.trim());
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Custom Compaction
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
pi.on("session_before_compact", async (event, ctx) => {
|
|
323
|
+
// Return a custom CompactionResult to override default compaction
|
|
324
|
+
const allMessages = [...event.messagesToSummarize, ...event.turnPrefixMessages];
|
|
325
|
+
const summary = await generateCustomSummary(allMessages);
|
|
326
|
+
return { summary, firstKeptEntryId: event.firstKeptEntryId, tokensBefore: event.tokens };
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Dynamic Tool Registration
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
pi.on("session_start", async () => {
|
|
334
|
+
pi.registerTool({ name: "echo", /* ... */ });
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
pi.registerCommand("add-tool", {
|
|
338
|
+
async handler(name, ctx) {
|
|
339
|
+
pi.registerTool({ name, /* ... */ });
|
|
340
|
+
ctx.ui.notify(`Registered tool: ${name}`, "info");
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
```
|