@counterposition/skill-pi 0.1.1 → 0.2.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/package.json +1 -1
- package/skills/pi/SKILL.md +58 -25
- package/skills/pi/references/extensions.md +149 -242
- package/skills/pi/references/packages.md +89 -59
- package/skills/pi/references/patterns.md +4 -4
- package/skills/pi/references/providers.md +101 -153
- package/skills/pi/references/sdk.md +105 -125
- package/skills/pi/references/settings.md +121 -99
- package/skills/pi/references/skills.md +83 -73
package/package.json
CHANGED
package/skills/pi/SKILL.md
CHANGED
|
@@ -7,13 +7,13 @@ compatibility: "Pi-specific guidance for agents that support Agent Skills"
|
|
|
7
7
|
|
|
8
8
|
# Pi Coding Agent
|
|
9
9
|
|
|
10
|
-
Pi is a minimal
|
|
10
|
+
Pi is a minimal terminal coding harness. It ships four default tools (`read`, `write`, `edit`, `bash`) and four execution modes (interactive TUI, print, JSON/RPC, SDK). Features like sub-agents, plan mode, permission flows, and MCP are intentionally left to extensions and Pi packages.
|
|
11
11
|
|
|
12
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
13
|
|
|
14
14
|
## Architecture at a Glance
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
The coding-agent stack centers on four core packages. The monorepo also includes adjacent packages such as `pi-web-ui`, `pi-mom`, and `pi-pods`.
|
|
17
17
|
|
|
18
18
|
| Package | npm | Purpose |
|
|
19
19
|
|---------|-----|---------|
|
|
@@ -76,6 +76,12 @@ Bundles of extensions, skills, prompts, and themes distributed via npm, git, or
|
|
|
76
76
|
|
|
77
77
|
Read `references/packages.md` for the package structure, `pi` manifest field, installation, and publishing.
|
|
78
78
|
|
|
79
|
+
### Context Files & Prompt Templates
|
|
80
|
+
|
|
81
|
+
Pi loads `AGENTS.md` or `CLAUDE.md` from the global agent dir and from `cwd` up through parent directories. `.pi/SYSTEM.md` replaces the default system prompt, and `APPEND_SYSTEM.md` appends to it. Prompt templates in `prompts/` directories become slash commands.
|
|
82
|
+
|
|
83
|
+
Read `references/settings.md` for discovery rules and config keys, and `references/packages.md` for how prompts/themes ship in packages.
|
|
84
|
+
|
|
79
85
|
### SDK
|
|
80
86
|
|
|
81
87
|
Programmatic embedding via `createAgentSession()`. Gives full control over models, tools, extensions, sessions, and event streaming.
|
|
@@ -117,7 +123,7 @@ Compaction walks backward from the newest message, keeping `keepRecentTokens` (d
|
|
|
117
123
|
| `find` | Find files by glob pattern |
|
|
118
124
|
| `ls` | List directory contents |
|
|
119
125
|
|
|
120
|
-
**
|
|
126
|
+
**Common tool sets:** `codingTools()` = `[read, bash, edit, write]`, `readOnlyTools()` = `[read, grep, find, ls]`. When you provide a custom `cwd` and explicit tools in the SDK, use `createCodingTools(cwd)` / `createReadOnlyTools(cwd)` so path resolution stays correct.
|
|
121
127
|
|
|
122
128
|
## Execution Modes
|
|
123
129
|
|
|
@@ -125,34 +131,60 @@ Compaction walks backward from the newest message, keeping `keepRecentTokens` (d
|
|
|
125
131
|
|------|-----------|----------|
|
|
126
132
|
| Interactive | `pi` | Full TUI with editor, streaming, keybindings |
|
|
127
133
|
| Print | `pi -p "prompt"` | Single-shot text output |
|
|
128
|
-
| JSON | `pi --mode json
|
|
134
|
+
| JSON | `pi --mode json "prompt"` | Newline-delimited event stream |
|
|
129
135
|
| RPC | `pi --mode rpc` | JSON-RPC over stdin/stdout for IDE integration |
|
|
130
136
|
| SDK | `createAgentSession()` | Embed in Node.js applications |
|
|
131
137
|
|
|
132
138
|
## Quick Reference: CLI Flags
|
|
133
139
|
|
|
134
140
|
```text
|
|
135
|
-
pi [options] [
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
--
|
|
147
|
-
--
|
|
148
|
-
--
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
141
|
+
pi [options] [@files...] [messages...]
|
|
142
|
+
|
|
143
|
+
# package commands
|
|
144
|
+
install <source> [-l]
|
|
145
|
+
remove <source> [-l]
|
|
146
|
+
uninstall <source> [-l]
|
|
147
|
+
update [source]
|
|
148
|
+
list
|
|
149
|
+
config
|
|
150
|
+
|
|
151
|
+
# modes and output
|
|
152
|
+
-p, --print
|
|
153
|
+
--mode json|rpc
|
|
154
|
+
--export <in> [out]
|
|
155
|
+
|
|
156
|
+
# model selection
|
|
157
|
+
--provider <name>
|
|
158
|
+
--model <pattern>
|
|
159
|
+
--api-key <key>
|
|
160
|
+
--thinking <level>
|
|
161
|
+
--models <patterns>
|
|
162
|
+
--list-models [search]
|
|
163
|
+
|
|
164
|
+
# sessions
|
|
165
|
+
-c, --continue
|
|
166
|
+
-r, --resume
|
|
167
|
+
--session <path|id>
|
|
168
|
+
--fork <path|id>
|
|
169
|
+
--session-dir <dir>
|
|
170
|
+
--no-session
|
|
171
|
+
|
|
172
|
+
# tools and resources
|
|
173
|
+
--tools <list>
|
|
174
|
+
--no-tools
|
|
175
|
+
-e, --extension <source>
|
|
176
|
+
--skill <path>
|
|
177
|
+
--prompt-template <path>
|
|
178
|
+
--theme <path>
|
|
179
|
+
--no-extensions
|
|
180
|
+
--no-skills
|
|
181
|
+
--no-prompt-templates
|
|
182
|
+
--no-themes
|
|
183
|
+
|
|
184
|
+
# prompts and misc
|
|
185
|
+
--system-prompt <text>
|
|
186
|
+
--append-system-prompt <text>
|
|
187
|
+
--verbose
|
|
156
188
|
```
|
|
157
189
|
|
|
158
190
|
## How to Use This Skill
|
|
@@ -164,6 +196,7 @@ When working with Pi, read the appropriate reference file for the task at hand:
|
|
|
164
196
|
| Writing an extension (tools, commands, events, UI) | `references/extensions.md` |
|
|
165
197
|
| Creating a skill (SKILL.md authoring) | `references/skills.md` |
|
|
166
198
|
| Configuring Pi (settings.json options) | `references/settings.md` |
|
|
199
|
+
| Context files, prompt templates, themes, and resource discovery | `references/settings.md` and `references/packages.md` |
|
|
167
200
|
| Building a shareable package | `references/packages.md` |
|
|
168
201
|
| Embedding Pi programmatically | `references/sdk.md` |
|
|
169
202
|
| Adding LLM providers or models | `references/providers.md` |
|
|
@@ -1,30 +1,42 @@
|
|
|
1
1
|
# Extensions
|
|
2
2
|
|
|
3
|
-
Extensions are TypeScript modules that
|
|
3
|
+
Extensions are TypeScript modules that extend Pi's behavior. They run with the user's full permissions, so treat them like normal application code.
|
|
4
4
|
|
|
5
5
|
## Placement & Discovery
|
|
6
6
|
|
|
7
|
-
| Location | Scope |
|
|
8
|
-
|
|
9
|
-
| `~/.pi/agent/extensions/*.ts` | Global
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
7
|
+
| Location | Scope |
|
|
8
|
+
|----------|-------|
|
|
9
|
+
| `~/.pi/agent/extensions/*.ts` | Global |
|
|
10
|
+
| `~/.pi/agent/extensions/*/index.ts` | Global |
|
|
11
|
+
| `.pi/extensions/*.ts` | Project |
|
|
12
|
+
| `.pi/extensions/*/index.ts` | Project |
|
|
13
|
+
| `settings.json` → `extensions` | Additional local paths |
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
Use `pi -e ./my-extension.ts` or `pi --extension ...` for quick tests. Put stable extensions in auto-discovered locations if you want `/reload` to pick them up.
|
|
15
16
|
|
|
16
|
-
##
|
|
17
|
+
## Common Imports
|
|
17
18
|
|
|
18
19
|
```typescript
|
|
19
20
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
21
|
+
import { Type } from "@sinclair/typebox";
|
|
22
|
+
import { StringEnum } from "@mariozechner/pi-ai";
|
|
23
|
+
import { Text } from "@mariozechner/pi-tui";
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- Use `@sinclair/typebox` for schemas
|
|
27
|
+
- Use `StringEnum` from `@mariozechner/pi-ai` for Google-compatible string enums
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
33
|
+
import { Type } from "@sinclair/typebox";
|
|
20
34
|
|
|
21
35
|
export default function (pi: ExtensionAPI) {
|
|
22
|
-
// Subscribe to lifecycle events
|
|
23
36
|
pi.on("session_start", async (_event, ctx) => {
|
|
24
|
-
ctx.ui.notify("Extension loaded
|
|
37
|
+
if (ctx.hasUI) ctx.ui.notify("Extension loaded", "info");
|
|
25
38
|
});
|
|
26
39
|
|
|
27
|
-
// Register a tool the LLM can call
|
|
28
40
|
pi.registerTool({
|
|
29
41
|
name: "greet",
|
|
30
42
|
label: "Greet",
|
|
@@ -32,312 +44,207 @@ export default function (pi: ExtensionAPI) {
|
|
|
32
44
|
parameters: Type.Object({
|
|
33
45
|
name: Type.String({ description: "Name to greet" }),
|
|
34
46
|
}),
|
|
35
|
-
async execute(_toolCallId, params
|
|
36
|
-
const { name } = params as { name: string };
|
|
47
|
+
async execute(_toolCallId, params) {
|
|
37
48
|
return {
|
|
38
|
-
content: [{ type: "text", text: `Hello, ${name}!` }],
|
|
39
|
-
details: {
|
|
49
|
+
content: [{ type: "text", text: `Hello, ${params.name}!` }],
|
|
50
|
+
details: {},
|
|
40
51
|
};
|
|
41
52
|
},
|
|
42
53
|
});
|
|
43
54
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
description: "Do something",
|
|
55
|
+
pi.registerCommand("hello", {
|
|
56
|
+
description: "Say hello",
|
|
47
57
|
async handler(args, ctx) {
|
|
48
|
-
ctx.ui.notify(`
|
|
58
|
+
if (ctx.hasUI) ctx.ui.notify(`Hello ${args || "world"}!`, "info");
|
|
49
59
|
},
|
|
50
60
|
});
|
|
51
61
|
}
|
|
52
62
|
```
|
|
53
63
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
```
|
|
64
|
+
## Event Surface
|
|
65
|
+
|
|
66
|
+
Useful session events:
|
|
67
|
+
|
|
68
|
+
- `session_directory`
|
|
69
|
+
- `session_start`
|
|
70
|
+
- `session_before_switch`
|
|
71
|
+
- `session_switch`
|
|
72
|
+
- `session_before_fork`
|
|
73
|
+
- `session_fork`
|
|
74
|
+
- `session_before_compact`
|
|
75
|
+
- `session_compact`
|
|
76
|
+
- `session_before_tree`
|
|
77
|
+
- `session_tree`
|
|
78
|
+
- `session_shutdown`
|
|
79
|
+
- `resources_discover`
|
|
80
|
+
|
|
81
|
+
Useful agent events:
|
|
82
|
+
|
|
83
|
+
- `input`
|
|
84
|
+
- `before_agent_start`
|
|
85
|
+
- `agent_start`
|
|
86
|
+
- `agent_end`
|
|
87
|
+
- `turn_start`
|
|
88
|
+
- `turn_end`
|
|
89
|
+
- `message_start`
|
|
90
|
+
- `message_update`
|
|
91
|
+
- `message_end`
|
|
92
|
+
- `context`
|
|
93
|
+
- `before_provider_request`
|
|
94
|
+
- `model_select`
|
|
95
|
+
|
|
96
|
+
Useful tool events:
|
|
97
|
+
|
|
98
|
+
- `tool_execution_start`
|
|
99
|
+
- `tool_call`
|
|
100
|
+
- `tool_execution_update`
|
|
101
|
+
- `tool_result`
|
|
102
|
+
- `tool_execution_end`
|
|
103
|
+
|
|
104
|
+
## Extension Context
|
|
105
|
+
|
|
106
|
+
`ctx` gives extensions access to:
|
|
107
|
+
|
|
108
|
+
- `ctx.ui` for interactive UI hooks
|
|
109
|
+
- `ctx.cwd`
|
|
110
|
+
- `ctx.session`
|
|
111
|
+
- `ctx.sessionManager`
|
|
112
|
+
- `ctx.modelRegistry`
|
|
113
|
+
- `ctx.hasUI`
|
|
114
|
+
- `ctx.isIdle()`
|
|
115
|
+
- `ctx.abort()`
|
|
116
|
+
- `ctx.getContextUsage()`
|
|
117
|
+
- `ctx.getSystemPrompt()`
|
|
124
118
|
|
|
125
119
|
## Tool Registration
|
|
126
120
|
|
|
127
121
|
```typescript
|
|
128
122
|
pi.registerTool({
|
|
129
|
-
name: "tool_name",
|
|
130
|
-
label: "Tool Name",
|
|
131
|
-
description: "What this tool does",
|
|
132
|
-
parameters: Type.Object({
|
|
123
|
+
name: "tool_name",
|
|
124
|
+
label: "Tool Name",
|
|
125
|
+
description: "What this tool does",
|
|
126
|
+
parameters: Type.Object({
|
|
133
127
|
arg1: Type.String({ description: "..." }),
|
|
134
|
-
arg2: Type.Optional(Type.Number()),
|
|
135
128
|
}),
|
|
136
129
|
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// onUpdate: callback for streaming partial results
|
|
142
|
-
// ctx: ExtensionContext
|
|
130
|
+
// Runs before schema validation. Useful for migrating old tool-call shapes.
|
|
131
|
+
prepareArguments(rawArgs) {
|
|
132
|
+
return rawArgs;
|
|
133
|
+
},
|
|
143
134
|
|
|
135
|
+
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
144
136
|
return {
|
|
145
137
|
content: [{ type: "text", text: "result" }],
|
|
146
|
-
details: {
|
|
138
|
+
details: { persisted: true },
|
|
147
139
|
};
|
|
148
140
|
},
|
|
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
141
|
});
|
|
160
142
|
```
|
|
161
143
|
|
|
162
|
-
|
|
144
|
+
Important rules:
|
|
163
145
|
|
|
164
|
-
1.
|
|
165
|
-
2.
|
|
166
|
-
3.
|
|
167
|
-
4.
|
|
168
|
-
5.
|
|
146
|
+
1. Truncate large output.
|
|
147
|
+
2. Respect `signal.aborted`.
|
|
148
|
+
3. Throw on failure instead of returning fake success.
|
|
149
|
+
4. Use the file-mutation queue for write/edit style tools.
|
|
150
|
+
5. Put reconstructable state in `details`; it persists in session history.
|
|
169
151
|
|
|
170
152
|
## UI Methods
|
|
171
153
|
|
|
172
|
-
|
|
154
|
+
Check `ctx.hasUI` first. Non-interactive modes may not support UI.
|
|
173
155
|
|
|
174
156
|
```typescript
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
const edited = await ctx.ui.editor("Edit this text", initialContent);
|
|
157
|
+
const choice = await ctx.ui.select("Pick one:", ["A", "B"]);
|
|
158
|
+
const ok = await ctx.ui.confirm("Delete?", "This cannot be undone");
|
|
159
|
+
const text = await ctx.ui.input("Name:", "default");
|
|
160
|
+
const edited = await ctx.ui.editor("Edit:", "prefilled text");
|
|
180
161
|
|
|
181
|
-
|
|
182
|
-
ctx.ui.notify("Something happened", "info"); // "info" | "warning" | "error"
|
|
162
|
+
ctx.ui.notify("Done", "info");
|
|
183
163
|
|
|
184
|
-
|
|
185
|
-
ctx.ui.
|
|
186
|
-
ctx.ui.setWidget("
|
|
187
|
-
ctx.ui.setFooter(
|
|
164
|
+
ctx.ui.setStatus("my-ext", "Processing...");
|
|
165
|
+
ctx.ui.setWorkingMessage("Thinking deeply...");
|
|
166
|
+
ctx.ui.setWidget("my-widget", ["Line 1", "Line 2"]);
|
|
167
|
+
ctx.ui.setFooter((tui, theme) => new Text(theme.fg("dim", "Custom footer"), 0, 0));
|
|
168
|
+
ctx.ui.setTitle("pi - custom");
|
|
188
169
|
|
|
189
|
-
// Editor control
|
|
190
170
|
ctx.ui.setEditorText("prefilled text");
|
|
191
|
-
ctx.ui.
|
|
171
|
+
const current = ctx.ui.getEditorText();
|
|
172
|
+
ctx.ui.pasteToEditor("extra text");
|
|
192
173
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
174
|
+
ctx.ui.setToolsExpanded(true);
|
|
175
|
+
|
|
176
|
+
const result = await ctx.ui.custom((tui, theme, keybindings, done) => {
|
|
177
|
+
return new Text("Press Enter", 0, 0);
|
|
178
|
+
}, { overlay: true });
|
|
197
179
|
```
|
|
198
180
|
|
|
199
|
-
|
|
181
|
+
Pi 0.64.0 also added `ctx.ui.setHiddenThinkingLabel(...)` so interactive extensions can customize the collapsed thinking label.
|
|
200
182
|
|
|
201
|
-
|
|
202
|
-
pi.registerCommand("commandname", {
|
|
203
|
-
description: "What this command does", // Shown in autocomplete
|
|
204
|
-
args: "optional args description",
|
|
183
|
+
## Commands, Shortcuts, Flags
|
|
205
184
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
},
|
|
210
|
-
|
|
211
|
-
// Handler receives ExtensionCommandContext (superset of ExtensionContext)
|
|
185
|
+
```typescript
|
|
186
|
+
pi.registerCommand("name", {
|
|
187
|
+
description: "Run a command",
|
|
212
188
|
async handler(args, ctx) {
|
|
213
|
-
await ctx.waitForIdle();
|
|
214
|
-
ctx.
|
|
215
|
-
ctx.fork(entryId); // Fork from entry
|
|
216
|
-
ctx.navigateTree(); // Open tree navigator
|
|
217
|
-
ctx.reload(); // Reload extensions
|
|
189
|
+
await ctx.waitForIdle();
|
|
190
|
+
ctx.reload();
|
|
218
191
|
},
|
|
219
192
|
});
|
|
220
|
-
```
|
|
221
193
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
pi.registerKeybinding({
|
|
226
|
-
key: "ctrl+shift+k",
|
|
227
|
-
description: "Do something",
|
|
228
|
-
handler: async (ctx) => { /* ... */ },
|
|
194
|
+
pi.registerShortcut("ctrl+x", {
|
|
195
|
+
description: "Do something quickly",
|
|
196
|
+
handler: async (ctx) => {},
|
|
229
197
|
});
|
|
230
|
-
```
|
|
231
198
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
```typescript
|
|
235
|
-
pi.registerFlag({
|
|
236
|
-
name: "--my-flag",
|
|
199
|
+
pi.registerFlag("my-flag", {
|
|
237
200
|
description: "Enable something",
|
|
238
|
-
handler: (value) => {
|
|
201
|
+
handler: (value) => {},
|
|
239
202
|
});
|
|
240
203
|
```
|
|
241
204
|
|
|
242
|
-
##
|
|
205
|
+
## Persistence & Process Hooks
|
|
243
206
|
|
|
244
|
-
|
|
207
|
+
Store extension state with custom session entries:
|
|
245
208
|
|
|
246
209
|
```typescript
|
|
247
|
-
// Write state
|
|
248
210
|
pi.appendEntry("my-extension-state", { key: "value" });
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Use lifecycle hooks to restore it:
|
|
249
214
|
|
|
250
|
-
|
|
215
|
+
```typescript
|
|
251
216
|
pi.on("session_start", async (_event, ctx) => {
|
|
252
|
-
const
|
|
253
|
-
for (const entry of branch.reverse()) {
|
|
217
|
+
for (const entry of ctx.sessionManager.getBranch().reverse()) {
|
|
254
218
|
if (entry.type === "custom" && entry.name === "my-extension-state") {
|
|
255
|
-
const state = entry.data;
|
|
256
|
-
// Restore from state
|
|
257
219
|
break;
|
|
258
220
|
}
|
|
259
221
|
}
|
|
260
222
|
});
|
|
261
223
|
```
|
|
262
224
|
|
|
263
|
-
## Provider
|
|
225
|
+
## Provider Integration
|
|
264
226
|
|
|
265
|
-
Extensions can register
|
|
227
|
+
Extensions can register providers:
|
|
266
228
|
|
|
267
229
|
```typescript
|
|
268
230
|
pi.registerProvider("my-provider", {
|
|
269
231
|
baseUrl: "https://api.example.com",
|
|
270
232
|
api: "openai-completions",
|
|
271
|
-
apiKey: "
|
|
272
|
-
models: [
|
|
273
|
-
{ id: "my-model", name: "My Model", contextWindow: 128000, maxTokens: 4096 },
|
|
274
|
-
],
|
|
233
|
+
apiKey: "MY_PROVIDER_KEY",
|
|
234
|
+
models: [{ id: "my-model", name: "My Model", contextWindow: 128000, maxTokens: 4096 }],
|
|
275
235
|
});
|
|
276
236
|
```
|
|
277
237
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
## Bash Execution
|
|
238
|
+
If you need auth for a specific model request, use:
|
|
281
239
|
|
|
282
240
|
```typescript
|
|
283
|
-
const
|
|
241
|
+
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
284
242
|
```
|
|
285
243
|
|
|
286
|
-
##
|
|
287
|
-
|
|
288
|
-
### Permission Gate
|
|
244
|
+
## Handy Patterns
|
|
289
245
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
```
|
|
246
|
+
- Permission gates via `tool_call`
|
|
247
|
+
- Prompt injection via `before_agent_start`
|
|
248
|
+
- Branch or compaction customization via `session_before_tree` / `session_before_compact`
|
|
249
|
+
- Provider payload inspection via `before_provider_request`
|
|
250
|
+
- Stateful tools via `details` plus `session_start` reconstruction
|