pi-rnd 0.2.1
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 +21 -0
- package/README.md +74 -0
- package/agents/rnd-builder.md +98 -0
- package/agents/rnd-integrator.md +104 -0
- package/agents/rnd-planner.md +208 -0
- package/agents/rnd-verifier.md +164 -0
- package/dist/doctor.js +166 -0
- package/dist/doctor.js.map +1 -0
- package/dist/gates/bash-discipline.js +27 -0
- package/dist/gates/bash-discipline.js.map +1 -0
- package/dist/gates/read-evidence-pack.js +23 -0
- package/dist/gates/read-evidence-pack.js.map +1 -0
- package/dist/gates/registry.js +24 -0
- package/dist/gates/registry.js.map +1 -0
- package/dist/gates/rnd-dir-required.js +31 -0
- package/dist/gates/rnd-dir-required.js.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator/prompts.js +58 -0
- package/dist/orchestrator/prompts.js.map +1 -0
- package/dist/orchestrator/rnd-dir.js +20 -0
- package/dist/orchestrator/rnd-dir.js.map +1 -0
- package/dist/orchestrator/spawn.js +67 -0
- package/dist/orchestrator/spawn.js.map +1 -0
- package/dist/orchestrator/start.js +195 -0
- package/dist/orchestrator/start.js.map +1 -0
- package/dist/orchestrator/state.js +15 -0
- package/dist/orchestrator/state.js.map +1 -0
- package/dist/orchestrator/types.js +2 -0
- package/dist/orchestrator/types.js.map +1 -0
- package/docs/PI-API.md +574 -0
- package/docs/PORTING.md +105 -0
- package/package.json +57 -0
- package/skills/fp-practices/SKILL.md +128 -0
- package/skills/fp-practices/bash.md +114 -0
- package/skills/fp-practices/duckdb.md +116 -0
- package/skills/fp-practices/elixir.md +115 -0
- package/skills/fp-practices/javascript.md +119 -0
- package/skills/fp-practices/koka.md +120 -0
- package/skills/fp-practices/lean.md +120 -0
- package/skills/fp-practices/postgresql.md +120 -0
- package/skills/fp-practices/python.md +120 -0
- package/skills/fp-practices/svelte.md +114 -0
- package/skills/kiss-practices/SKILL.md +41 -0
- package/skills/kiss-practices/bash.md +70 -0
- package/skills/kiss-practices/duckdb.md +30 -0
- package/skills/kiss-practices/elixir.md +38 -0
- package/skills/kiss-practices/javascript.md +43 -0
- package/skills/kiss-practices/koka.md +34 -0
- package/skills/kiss-practices/lean.md +45 -0
- package/skills/kiss-practices/markdown.md +20 -0
- package/skills/kiss-practices/postgresql.md +31 -0
- package/skills/kiss-practices/python.md +64 -0
- package/skills/kiss-practices/svelte.md +59 -0
- package/skills/rnd-building/SKILL.md +256 -0
- package/skills/rnd-decomposition/SKILL.md +188 -0
- package/skills/rnd-experiments/SKILL.md +197 -0
- package/skills/rnd-failure-modes/SKILL.md +222 -0
- package/skills/rnd-iteration/SKILL.md +170 -0
- package/skills/rnd-orchestration/SKILL.md +314 -0
- package/skills/rnd-scaling/SKILL.md +188 -0
- package/skills/rnd-verification/SKILL.md +248 -0
- package/skills/using-rnd-framework/SKILL.md +65 -0
package/docs/PI-API.md
ADDED
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
# PI Extension API Reference
|
|
2
|
+
|
|
3
|
+
> Citation format: `(file:line-range)` — paths abbreviated as `pi-agent/` for
|
|
4
|
+
> `@earendil-works/pi-coding-agent/dist/core/` and `pi-sub/` for
|
|
5
|
+
> `@tintinweb/pi-subagents/src/`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. PI Runtime API (ExtensionAPI)
|
|
10
|
+
|
|
11
|
+
Extensions export a default factory function `(pi: ExtensionAPI) => void | Promise<void>`
|
|
12
|
+
(`pi-agent/extensions/types.d.ts:983`). PI awaits the factory before firing `session_start`.
|
|
13
|
+
Extensions load via **jiti** — TypeScript works without a compile step.
|
|
14
|
+
|
|
15
|
+
**Auto-discovery locations** (`pi-agent/` docs/extensions.md:7):
|
|
16
|
+
|
|
17
|
+
- `~/.pi/agent/extensions/*.ts` or `/*/index.ts` (global)
|
|
18
|
+
- `.pi/extensions/*.ts` or `/*/index.ts` (project-local)
|
|
19
|
+
- Packages listed in `settings.json#packages` or `settings.json#extensions`
|
|
20
|
+
- npm packages: `"pi": { "extensions": [...] }` in `package.json`
|
|
21
|
+
|
|
22
|
+
### ExtensionAPI method surface
|
|
23
|
+
|
|
24
|
+
(`pi-agent/extensions/types.d.ts:770-923`)
|
|
25
|
+
|
|
26
|
+
| Method | Signature | Notes |
|
|
27
|
+
|--------|-----------|-------|
|
|
28
|
+
| `pi.on(event, handler)` | overloaded per event type | subscribe lifecycle events |
|
|
29
|
+
| `pi.registerTool(tool)` | `(ToolDefinition) => void` | LLM-callable tools |
|
|
30
|
+
| `pi.registerCommand(name, opts)` | `(string, Omit<RegisteredCommand, "name"\|"sourceInfo">) => void` | slash commands |
|
|
31
|
+
| `pi.registerShortcut(shortcut, opts)` | `(KeyId, {handler}) => void` | keyboard shortcuts |
|
|
32
|
+
| `pi.registerFlag(name, opts)` | `(string, {type, default?}) => void` | CLI flags |
|
|
33
|
+
| `pi.getFlag(name)` | `() => boolean\|string\|undefined` | read flag value |
|
|
34
|
+
| `pi.registerMessageRenderer(type, fn)` | custom chat message renderer | |
|
|
35
|
+
| `pi.registerProvider(name, config)` | add/override model provider | can be called after init |
|
|
36
|
+
| `pi.unregisterProvider(name)` | remove provider | |
|
|
37
|
+
| `pi.sendMessage(msg, opts?)` | send custom message (not user turn) | |
|
|
38
|
+
| `pi.sendUserMessage(content, opts?)` | always triggers LLM turn | |
|
|
39
|
+
| `pi.appendEntry(type, data?)` | persist state in session (not sent to LLM) | |
|
|
40
|
+
| `pi.setSessionName(name)` / `getSessionName()` | session display name | |
|
|
41
|
+
| `pi.setLabel(entryId, label)` | bookmark/navigation labels | |
|
|
42
|
+
| `pi.exec(cmd, args, opts?)` | shell execution | |
|
|
43
|
+
| `pi.getActiveTools()` / `setActiveTools(names)` / `getAllTools()` | tool list management | |
|
|
44
|
+
| `pi.getCommands()` | list registered slash commands | |
|
|
45
|
+
| `pi.setModel(model)` / `getThinkingLevel()` / `setThinkingLevel(level)` | model control | |
|
|
46
|
+
| `pi.events` | shared `EventBus` for cross-extension comms | |
|
|
47
|
+
|
|
48
|
+
### EventBus shape
|
|
49
|
+
|
|
50
|
+
(`pi-agent/event-bus.d.ts:1-4`)
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
interface EventBus {
|
|
54
|
+
emit(channel: string, data: unknown): void;
|
|
55
|
+
on(channel: string, handler: (data: unknown) => void): () => void; // returns unsub fn
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### pi.on() — all supported events
|
|
60
|
+
|
|
61
|
+
(`pi-agent/extensions/types.d.ts:771-798`)
|
|
62
|
+
|
|
63
|
+
| Event name | Handler return | When fired |
|
|
64
|
+
|------------|---------------|------------|
|
|
65
|
+
| `resources_discover` | `ResourcesDiscoverResult?` | after `session_start`, for extra skill/prompt/theme paths |
|
|
66
|
+
| `session_start` | void | startup / reload / new / resume / fork |
|
|
67
|
+
| `session_before_switch` | `{cancel?}` | before switching session |
|
|
68
|
+
| `session_before_fork` | `{cancel?, skipConversationRestore?}` | before forking |
|
|
69
|
+
| `session_before_compact` | `{cancel?, compaction?}` | before context compaction |
|
|
70
|
+
| `session_compact` | void | after compaction |
|
|
71
|
+
| `session_shutdown` | void | before teardown (quit/reload/replace) |
|
|
72
|
+
| `session_before_tree` | `{cancel?, summary?, ...}` | before tree navigation |
|
|
73
|
+
| `session_tree` | void | after tree navigation |
|
|
74
|
+
| `context` | `{messages?}` | before each LLM call; can mutate messages |
|
|
75
|
+
| `before_provider_request` | `unknown` | before HTTP request to provider |
|
|
76
|
+
| `after_provider_response` | void | after HTTP response received |
|
|
77
|
+
| `before_agent_start` | `{message?, systemPrompt?}` | after user submit, before agent loop |
|
|
78
|
+
| `agent_start` | void | agent loop begins |
|
|
79
|
+
| `agent_end` | void | agent loop ends |
|
|
80
|
+
| `turn_start` | void | each turn starts |
|
|
81
|
+
| `turn_end` | void | each turn ends |
|
|
82
|
+
| `message_start` / `message_update` / `message_end` | void | message streaming |
|
|
83
|
+
| `tool_execution_start` / `tool_execution_update` / `tool_execution_end` | void | tool running |
|
|
84
|
+
| `model_select` | void | model changed |
|
|
85
|
+
| `tool_call` | `{block?, reason?}` | before tool executes; mutate `event.input` to patch args |
|
|
86
|
+
| `tool_result` | `{content?, details?, isError?}` | after tool executes; can rewrite result |
|
|
87
|
+
| `user_bash` | `{operations?, result?}` | user's `!cmd` or `!!cmd` |
|
|
88
|
+
| `input` | `{action: "continue"\|"transform"\|"handled"}` | user input received |
|
|
89
|
+
|
|
90
|
+
### ToolDefinition fields
|
|
91
|
+
|
|
92
|
+
(`pi-agent/extensions/types.d.ts:325-356`)
|
|
93
|
+
|
|
94
|
+
Required: `name`, `label`, `description`, `parameters` (TypeBox schema), `execute`.
|
|
95
|
+
Optional: `promptSnippet`, `promptGuidelines`, `executionMode` (`"sequential"`|`"parallel"`),
|
|
96
|
+
`renderCall`, `renderResult`, `renderShell`, `prepareArguments`.
|
|
97
|
+
|
|
98
|
+
### ProviderConfig (pi.registerProvider)
|
|
99
|
+
|
|
100
|
+
(`pi-agent/extensions/types.d.ts:924-953`)
|
|
101
|
+
|
|
102
|
+
Fields: `baseUrl?`, `apiKey?`, `api?`, `models?`, `headers?`, `authHeader?`,
|
|
103
|
+
`streamSimple?`, `oauth?`. If `models` provided, replaces all models for that provider.
|
|
104
|
+
Provider registration is queued during extension init, applied once runner binds context;
|
|
105
|
+
safe to call at any time after that.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 2. pi-subagents RPC Contract
|
|
110
|
+
|
|
111
|
+
RPC is implemented over `pi.events` using scoped channels.
|
|
112
|
+
(`pi-sub/cross-extension-rpc.ts:1-95`)
|
|
113
|
+
|
|
114
|
+
### Protocol
|
|
115
|
+
|
|
116
|
+
- `PROTOCOL_VERSION = 2` (`pi-sub/cross-extension-rpc.ts:24`)
|
|
117
|
+
- Request: emit on `subagents:rpc:<method>` with `{ requestId: string, ...params }`
|
|
118
|
+
- Reply: listen on `subagents:rpc:<method>:reply:<requestId>`
|
|
119
|
+
- Reply envelope (`pi-sub/cross-extension-rpc.ts:19-21`):
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
type RpcReply<T = void> =
|
|
123
|
+
| { success: true; data?: T }
|
|
124
|
+
| { success: false; error: string };
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Methods
|
|
128
|
+
|
|
129
|
+
**ping** (`pi-sub/cross-extension-rpc.ts:76-78`):
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
Request: { requestId: string }
|
|
133
|
+
Reply: { success: true, data: { version: 2 } }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**spawn** (`pi-sub/cross-extension-rpc.ts:80-85`):
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
Request: { requestId: string, type: string, prompt: string, options?: any }
|
|
140
|
+
Reply: { success: true, data: { id: string } }
|
|
141
|
+
Error: { success: false, error: "No active session" }
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Agent ID is `randomUUID().slice(0, 17)` (`pi-sub/agent-manager.ts:92`).
|
|
145
|
+
The `type` field is a SubagentType string (matches an agent definition name).
|
|
146
|
+
Spawn returns immediately; agent may still be queued (see section 3).
|
|
147
|
+
|
|
148
|
+
**stop** (`pi-sub/cross-extension-rpc.ts:88-91`):
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
Request: { requestId: string, agentId: string }
|
|
152
|
+
Reply: { success: true }
|
|
153
|
+
Error: { success: false, error: "Agent not found" }
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Handler wiring
|
|
157
|
+
|
|
158
|
+
`registerRpcHandlers(deps)` is called once during extension init.
|
|
159
|
+
Returns `{ unsubPing, unsubSpawn, unsubStop }` for cleanup.
|
|
160
|
+
(`pi-sub/cross-extension-rpc.ts:73-95`)
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 3. pi-subagents Lifecycle Events
|
|
165
|
+
|
|
166
|
+
All events emitted on `pi.events`. Sources: `pi-sub/index.ts:364-411`.
|
|
167
|
+
|
|
168
|
+
| Event | Payload | When fired |
|
|
169
|
+
|-------|---------|------------|
|
|
170
|
+
| `subagents:ready` | `{}` | once on extension init |
|
|
171
|
+
| `subagents:settings_loaded` | `{ settings: SubagentsSettings }` | settings loaded at init |
|
|
172
|
+
| `subagents:settings_changed` | `{ settings: SubagentsSettings, persisted: boolean }` | settings mutated via /agents |
|
|
173
|
+
| `subagents:created` | `{ id, type, description, isBackground: true }` | background spawn accepted |
|
|
174
|
+
| `subagents:started` | `{ id, type, description }` | agent transitions queued→running |
|
|
175
|
+
| `subagents:completed` | see below | terminal success |
|
|
176
|
+
| `subagents:failed` | same shape as completed | terminal failure (error/stopped/aborted) |
|
|
177
|
+
| `subagents:steered` | `{ id, message }` | `steer_subagent` tool called |
|
|
178
|
+
|
|
179
|
+
**completed / failed payload** (`pi-sub/index.ts:338-362`):
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
{
|
|
183
|
+
id: string;
|
|
184
|
+
type: string;
|
|
185
|
+
description: string;
|
|
186
|
+
result: string | undefined;
|
|
187
|
+
error: string | undefined;
|
|
188
|
+
status: "completed" | "error" | "stopped" | "aborted" | "steered";
|
|
189
|
+
toolUses: number;
|
|
190
|
+
durationMs: number;
|
|
191
|
+
tokens: { input: number; output: number; total: number } | undefined;
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Timing: subagents:completed vs spawn-RPC reply
|
|
196
|
+
|
|
197
|
+
Background `subagents:rpc:spawn` reply arrives immediately, carrying only `{ id }`.
|
|
198
|
+
The agent may be queued (`status: "queued"`) at that point.
|
|
199
|
+
`subagents:started` fires when it leaves the queue.
|
|
200
|
+
`subagents:completed` fires inside the AgentManager `onComplete` callback, after the agent
|
|
201
|
+
finishes. There is no ordering guarantee relative to the spawn reply — that reply arrived
|
|
202
|
+
before the agent even started. (`pi-sub/agent-manager.ts:62-100`, `pi-sub/index.ts:364-411`)
|
|
203
|
+
|
|
204
|
+
### Transcript readability mid-flight
|
|
205
|
+
|
|
206
|
+
The output file is flushed to disk on every `turn_end` event during the agent's run.
|
|
207
|
+
(`pi-sub/output-file.ts:69-71`: `session.subscribe(event => { if (event.type === "turn_end") flush(); })`)
|
|
208
|
+
Transcripts are readable mid-flight; no need to wait for completion.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 4. Custom Agent Frontmatter Schema
|
|
213
|
+
|
|
214
|
+
Custom agents defined as `.md` files in `.pi/agents/<name>.md` (project) or
|
|
215
|
+
`~/.pi/agent/agents/<name>.md` (global). Project overrides global on name collision.
|
|
216
|
+
(`pi-sub/custom-agents.ts:20-28`)
|
|
217
|
+
|
|
218
|
+
Parsed via `parseFrontmatter()` from `@earendil-works/pi-coding-agent`.
|
|
219
|
+
Body (after frontmatter block) becomes `systemPrompt`. (`pi-sub/custom-agents.ts:51-52`)
|
|
220
|
+
|
|
221
|
+
### All supported frontmatter fields
|
|
222
|
+
|
|
223
|
+
(`pi-sub/custom-agents.ts:53-73`)
|
|
224
|
+
|
|
225
|
+
| Field | Type | Default | Notes |
|
|
226
|
+
|-------|------|---------|-------|
|
|
227
|
+
| `display_name` | string | — | human-readable name |
|
|
228
|
+
| `description` | string | filename stem | shown in UI / system prompt |
|
|
229
|
+
| `tools` | CSV string | BUILTIN_TOOL_NAMES | `"none"` → empty list; omit → all builtins |
|
|
230
|
+
| `disallowed_tools` | CSV string | — | tools blocked for this agent |
|
|
231
|
+
| `extensions` / `inherit_extensions` | bool or CSV | `true` (inherit all) | `false`/`"none"` → none; CSV → named list |
|
|
232
|
+
| `skills` / `inherit_skills` | bool or CSV | `true` (inherit all) | same semantics |
|
|
233
|
+
| `model` | string | — | model ID override |
|
|
234
|
+
| `thinking` | ThinkingLevel string | — | thinking level override |
|
|
235
|
+
| `max_turns` | integer ≥ 0 | — | 0 = unlimited |
|
|
236
|
+
| `prompt_mode` | `"replace"` \| `"append"` | `"replace"` | system prompt source |
|
|
237
|
+
| `inherit_context` | boolean | — | prepend parent conversation to prompt text |
|
|
238
|
+
| `run_in_background` | boolean | — | default run mode |
|
|
239
|
+
| `isolated` | boolean | — | deprecated alias for `isolation: worktree` |
|
|
240
|
+
| `memory` | `"user"` \| `"project"` \| `"local"` | — | memory scope |
|
|
241
|
+
| `isolation` | `"worktree"` | — | creates a temp git worktree |
|
|
242
|
+
| `enabled` | boolean | `true` | `false` disables the agent |
|
|
243
|
+
|
|
244
|
+
**`prompt_mode` × `inherit_context` interaction:**
|
|
245
|
+
`prompt_mode: "replace"` means the agent's body replaces the parent's system prompt
|
|
246
|
+
entirely. `inherit_context: true` is orthogonal: it prepends the parent's conversation
|
|
247
|
+
history to the prompt text. They affect different things — system prompt source vs
|
|
248
|
+
conversation context — and can be combined freely. (`pi-sub/custom-agents.ts:65-66`,
|
|
249
|
+
exploration cache `pi-subagents.md:54-57`)
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## 5. Slash Command Registration
|
|
254
|
+
|
|
255
|
+
Extensions register slash commands at load time via:
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
pi.registerCommand(name, {
|
|
259
|
+
description?: string,
|
|
260
|
+
getArgumentCompletions?: (prefix: string) => AutocompleteItem[] | null | Promise<...>,
|
|
261
|
+
handler: (args: string, ctx: ExtensionCommandContext) => Promise<void>,
|
|
262
|
+
});
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
(`pi-agent/extensions/types.d.ts:802`, `RegisteredCommand` interface at line 755-761`)
|
|
266
|
+
|
|
267
|
+
`name` is the command name **without** the leading `/`. The runtime prepends `/` when
|
|
268
|
+
displaying and routing. (`pi-agent/docs/extensions.md` — example: `pi.registerCommand("mycommand", ...)`)
|
|
269
|
+
|
|
270
|
+
The handler receives `args: string` (everything after the command name) and a full
|
|
271
|
+
`ExtensionCommandContext` with `ctx.ui`, session navigation (`newSession`, `fork`,
|
|
272
|
+
`switchSession`), `ctx.waitForIdle()`, and `ctx.reload()`.
|
|
273
|
+
(`pi-agent/extensions/types.d.ts:238-273`)
|
|
274
|
+
|
|
275
|
+
Commands registered by `@tintinweb/pi-subagents`: `/agents` (`pi-sub/index.ts` — command
|
|
276
|
+
registered in the extension factory body).
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## 6. Settings File Locations and Schema
|
|
281
|
+
|
|
282
|
+
### pi (host) settings
|
|
283
|
+
|
|
284
|
+
File locations (`pi-agent/settings-manager.d.ts:103-108`):
|
|
285
|
+
|
|
286
|
+
- Global: `~/.pi/agent/settings.json` (default; `$PI_CODING_AGENT_DIR` overrides the
|
|
287
|
+
entire agent dir path)
|
|
288
|
+
- Project: `.pi/settings.json` (cwd)
|
|
289
|
+
- Precedence: project overrides global on a per-field basis
|
|
290
|
+
|
|
291
|
+
**Settings schema** (`pi-agent/settings-manager.d.ts:57-94`):
|
|
292
|
+
|
|
293
|
+
| Key | Type | Notes |
|
|
294
|
+
|-----|------|-------|
|
|
295
|
+
| `defaultProvider` | string | |
|
|
296
|
+
| `defaultModel` | string | |
|
|
297
|
+
| `defaultThinkingLevel` | `"off"\|"minimal"\|"low"\|"medium"\|"high"\|"xhigh"` | |
|
|
298
|
+
| `transport` | Transport | |
|
|
299
|
+
| `steeringMode` | `"all"\|"one-at-a-time"` | |
|
|
300
|
+
| `followUpMode` | `"all"\|"one-at-a-time"` | |
|
|
301
|
+
| `theme` | string | |
|
|
302
|
+
| `compaction` | `{enabled?, reserveTokens?, keepRecentTokens?}` | |
|
|
303
|
+
| `branchSummary` | `{reserveTokens?, skipPrompt?}` | |
|
|
304
|
+
| `retry` | `{enabled?, maxRetries?, baseDelayMs?, provider?}` | |
|
|
305
|
+
| `hideThinkingBlock` | boolean | |
|
|
306
|
+
| `shellPath` | string | |
|
|
307
|
+
| `lastChangelogVersion` | string | last seen changelog version |
|
|
308
|
+
| `quietStartup` | boolean | suppress startup output |
|
|
309
|
+
| `shellCommandPrefix` | string | prefix prepended to shell commands |
|
|
310
|
+
| `npmCommand` | `string[]` | override npm executable and args |
|
|
311
|
+
| `collapseChangelog` | boolean | collapse changelog UI on startup |
|
|
312
|
+
| `enableInstallTelemetry` | boolean | opt-in install telemetry |
|
|
313
|
+
| `doubleEscapeAction` | `"fork"\|"tree"\|"none"` | action triggered by double-Escape |
|
|
314
|
+
| `treeFilterMode` | `"default"\|"no-tools"\|"user-only"\|"labeled-only"\|"all"` | session tree display filter |
|
|
315
|
+
| `editorPaddingX` | number | horizontal editor padding in cells |
|
|
316
|
+
| `autocompleteMaxVisible` | number | max autocomplete suggestions shown |
|
|
317
|
+
| `showHardwareCursor` | boolean | show hardware cursor in TUI |
|
|
318
|
+
| `packages` | `PackageSource[]` | npm/git packages to load extensions/skills from |
|
|
319
|
+
| `extensions` | `string[]` | extra extension paths |
|
|
320
|
+
| `skills` | `string[]` | extra skill paths |
|
|
321
|
+
| `prompts` | `string[]` | extra prompt template paths |
|
|
322
|
+
| `themes` | `string[]` | extra theme paths |
|
|
323
|
+
| `enableSkillCommands` | boolean | |
|
|
324
|
+
| `sessionDir` | string | override for sessions directory |
|
|
325
|
+
| `enabledModels` | `string[]` | |
|
|
326
|
+
| `terminal` | `{showImages?, imageWidthCells?, clearOnShrink?, showTerminalProgress?}` | |
|
|
327
|
+
| `images` | `{autoResize?, blockImages?}` | |
|
|
328
|
+
| `thinkingBudgets` | `{minimal?, low?, medium?, high?}` | |
|
|
329
|
+
| `markdown` | `{codeBlockIndent?}` | |
|
|
330
|
+
| `warnings` | `{anthropicExtraUsage?}` | |
|
|
331
|
+
|
|
332
|
+
### pi-subagents settings
|
|
333
|
+
|
|
334
|
+
(`pi-sub/settings.ts:1-20`, `pi-sub/settings.ts:74-80`)
|
|
335
|
+
|
|
336
|
+
- Global: `~/.pi/agent/subagents.json` — manual defaults, never written by the extension
|
|
337
|
+
- Project: `.pi/subagents.json` — written by `/agents → Settings`
|
|
338
|
+
- Precedence: project overrides global (`pi-sub/settings.ts:99-100`)
|
|
339
|
+
|
|
340
|
+
**SubagentsSettings schema:**
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
interface SubagentsSettings {
|
|
344
|
+
maxConcurrent?: number; // default 4; 1–1024
|
|
345
|
+
defaultMaxTurns?: number; // 0 = unlimited; 0–10000
|
|
346
|
+
graceTurns?: number; // 1–1000
|
|
347
|
+
defaultJoinMode?: "async" | "group" | "smart";
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## 7. Skill Discovery
|
|
354
|
+
|
|
355
|
+
Skills are `.md` files injected into the system prompt as XML-formatted context blocks.
|
|
356
|
+
|
|
357
|
+
### Discovery order
|
|
358
|
+
|
|
359
|
+
(`pi-agent/skills.d.ts:46-58`, exploration cache `settings-and-skills.md:28-43`)
|
|
360
|
+
|
|
361
|
+
1. `~/.pi/agent/skills/` — root `.md` files discovered individually; SKILL.md dirs recursed
|
|
362
|
+
2. `~/.agents/skills/` — SKILL.md dirs only (root `.md` files NOT auto-discovered here)
|
|
363
|
+
3. `.pi/skills/` — root `.md` files + SKILL.md dirs
|
|
364
|
+
4. `.agents/skills/` in cwd and ancestor dirs up to git root — SKILL.md dirs only
|
|
365
|
+
5. npm packages: `"pi": { "skills": [...] }` in `package.json`, or `skills/` dir in package
|
|
366
|
+
6. `settings.json#skills` array — explicit paths
|
|
367
|
+
|
|
368
|
+
### Package manifest
|
|
369
|
+
|
|
370
|
+
For npm-published extensions, place in `package.json`:
|
|
371
|
+
|
|
372
|
+
```json
|
|
373
|
+
{
|
|
374
|
+
"pi": {
|
|
375
|
+
"extensions": ["./dist/index.js"],
|
|
376
|
+
"skills": ["./skills"]
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
(`pi-agent/docs/extensions.md` — extension auto-discovery via `pi.extensions` in package.json)
|
|
382
|
+
|
|
383
|
+
### Skill file rules
|
|
384
|
+
|
|
385
|
+
(`pi-agent/skills.d.ts:26-33`)
|
|
386
|
+
|
|
387
|
+
- Directory containing `SKILL.md` → treated as one skill root; recursion stops there
|
|
388
|
+
- Otherwise: direct `.md` children of a skills dir are each a skill
|
|
389
|
+
- Sub-directories without `SKILL.md` are recursed to find `SKILL.md` files
|
|
390
|
+
|
|
391
|
+
### Skill frontmatter
|
|
392
|
+
|
|
393
|
+
(`pi-agent/skills.d.ts:3-7`)
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
interface SkillFrontmatter {
|
|
397
|
+
name?: string;
|
|
398
|
+
description?: string;
|
|
399
|
+
"disable-model-invocation"?: boolean;
|
|
400
|
+
[key: string]: unknown; // open record — arbitrary additional keys permitted (skills.d.ts:7)
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
`disable-model-invocation: true` excludes the skill from the system prompt; it can still
|
|
405
|
+
be invoked explicitly via `/skill:<name>`.
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## 8. Session / Output File Locations
|
|
410
|
+
|
|
411
|
+
### PI session files
|
|
412
|
+
|
|
413
|
+
Sessions stored in `~/.pi/agent/sessions/` by default.
|
|
414
|
+
Override via `settings.json#sessionDir`. (`pi-agent/settings-manager.d.ts:93`)
|
|
415
|
+
|
|
416
|
+
### pi-subagents output files
|
|
417
|
+
|
|
418
|
+
Path template (`pi-sub/output-file.ts:15-23`):
|
|
419
|
+
|
|
420
|
+
```
|
|
421
|
+
/tmp/pi-subagents-<uid>/<cwd-encoded>/<sessionId>/tasks/<agentId>.output
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Where `<cwd-encoded>` = `cwd.replace(/\//g, "-").replace(/^-/, "")`.
|
|
425
|
+
Directory permissions: `0700`. (`pi-sub/output-file.ts:17-19`)
|
|
426
|
+
|
|
427
|
+
**Format:** JSONL, one entry per message. Each entry:
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
{
|
|
431
|
+
isSidechain: true,
|
|
432
|
+
agentId: string,
|
|
433
|
+
type: "user" | "assistant" | "toolResult",
|
|
434
|
+
message: AgentMessage,
|
|
435
|
+
timestamp: string, // ISO 8601
|
|
436
|
+
cwd: string,
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
(`pi-sub/output-file.ts:26-36`, `pi-sub/output-file.ts:52-64`)
|
|
441
|
+
|
|
442
|
+
Initial user prompt written by `writeInitialEntry()` at spawn time.
|
|
443
|
+
Subsequent messages appended by `streamToOutputFile()` on each `turn_end`.
|
|
444
|
+
Final flush performed on agent cleanup. (`pi-sub/output-file.ts:42-77`)
|
|
445
|
+
|
|
446
|
+
File path available in `AgentRecord.outputFile` for cross-extension access.
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## 9. Resolved Open Questions
|
|
451
|
+
|
|
452
|
+
### Q1: Slash command registration mechanism
|
|
453
|
+
|
|
454
|
+
Slash commands are registered via `pi.registerCommand(name, opts)` where `name` has no
|
|
455
|
+
leading `/`. (`pi-agent/extensions/types.d.ts:802`) The runtime routes `/name` user
|
|
456
|
+
input to the handler. This is confirmed by `@tintinweb/pi-subagents` registering `/agents`
|
|
457
|
+
this way in its extension factory (`pi-sub/index.ts` — `pi.registerCommand("agents", ...)`
|
|
458
|
+
pattern visible from imports and factory body). Commands are TypeScript — there are no
|
|
459
|
+
`.md`-based slash command definitions. The `/` prefix is entirely runtime-managed; the
|
|
460
|
+
extension only ever sees and registers the bare name.
|
|
461
|
+
|
|
462
|
+
### Q2: subagents:completed timing relative to spawn-RPC reply
|
|
463
|
+
|
|
464
|
+
The spawn RPC reply (`{ success: true, data: { id } }`) arrives immediately after
|
|
465
|
+
`manager.spawn()` returns, before the agent executes any work. (`pi-sub/cross-extension-rpc.ts:81-85`)
|
|
466
|
+
The `spawn()` method assigns an ID, sets status to `"queued"` for background agents, and
|
|
467
|
+
returns synchronously. (`pi-sub/agent-manager.ts:92-98`) `subagents:completed` fires inside
|
|
468
|
+
the `onComplete` callback passed to `AgentManager`, which is called only after the agent
|
|
469
|
+
finishes all turns and cleans up. (`pi-sub/index.ts:364-373`) These two events are separated
|
|
470
|
+
by the full agent execution time. Importantly, the `onComplete` callback fires after the
|
|
471
|
+
agent promise resolves, not before — so a post-hoc audit listener on `subagents:completed`
|
|
472
|
+
can inspect the output file, but cannot veto or block the result from reaching the parent;
|
|
473
|
+
by the time `completed` fires, the agent has already returned its result string.
|
|
474
|
+
|
|
475
|
+
### Q3: Transcript mid-flight readability
|
|
476
|
+
|
|
477
|
+
Transcripts are readable mid-flight. The output file is opened at spawn time
|
|
478
|
+
(`writeInitialEntry` at `pi-sub/output-file.ts:26-36`) and flushed to disk on every
|
|
479
|
+
`turn_end` during the agent's run (`pi-sub/output-file.ts:69-71`). Because flush is
|
|
480
|
+
synchronous (`appendFileSync`), each turn's messages are durably written before the next
|
|
481
|
+
turn begins. A reader monitoring the file path (`AgentRecord.outputFile`) sees new entries
|
|
482
|
+
accumulate turn-by-turn during execution, not just at completion.
|
|
483
|
+
|
|
484
|
+
### Q4: prompt_mode: replace × inherit_context: true interaction
|
|
485
|
+
|
|
486
|
+
`prompt_mode` and `inherit_context` are orthogonal fields affecting different aspects of
|
|
487
|
+
agent context. (`pi-sub/custom-agents.ts:65-66`)
|
|
488
|
+
|
|
489
|
+
`prompt_mode: "replace"` (the default) means the agent's `.md` body is used as the full
|
|
490
|
+
system prompt, replacing whatever the parent agent's system prompt was. `prompt_mode: "append"`
|
|
491
|
+
appends the body to the parent system prompt instead.
|
|
492
|
+
|
|
493
|
+
`inherit_context: true` means the parent's conversation history (the messages array, not
|
|
494
|
+
the system prompt) is prepended to the agent's context when it starts. It is independent of
|
|
495
|
+
which system prompt source is used.
|
|
496
|
+
|
|
497
|
+
Combined effect of `prompt_mode: "replace"` + `inherit_context: true`: the agent gets the
|
|
498
|
+
parent's conversation history as context but runs under its own system prompt (the `.md`
|
|
499
|
+
body), not the parent's. This is the planner pattern: planner reads what's been discussed
|
|
500
|
+
but operates under its own instructions.
|
|
501
|
+
|
|
502
|
+
### Q5: Full pi extension runtime API surface
|
|
503
|
+
|
|
504
|
+
The surface is `ExtensionAPI` defined in `pi-agent/extensions/types.d.ts:770-923`. Complete
|
|
505
|
+
method list documented in Section 1. Key observations that differ from prior assumptions:
|
|
506
|
+
|
|
507
|
+
- Tool interception is via `tool_call` / `tool_result` events with mutable `event.input`
|
|
508
|
+
and result override — not named PreToolUse/PostToolUse hooks.
|
|
509
|
+
- No file-specific event hooks (no Read-event, Write-event, etc.) — only a generic
|
|
510
|
+
`tool_call` event that carries `toolName` and `input`, requiring the handler to check
|
|
511
|
+
`toolName` itself.
|
|
512
|
+
- `pi.events` is the shared `EventBus` instance for cross-extension communication, exposed
|
|
513
|
+
directly on the `ExtensionAPI` object (`pi-agent/extensions/types.d.ts:922`).
|
|
514
|
+
- Provider registration (`registerProvider`/`unregisterProvider`) supports OAuth, custom
|
|
515
|
+
stream handlers, and model-level API overrides — more complete than expected.
|
|
516
|
+
- Session navigation (fork, branch tree, compaction control) is available in
|
|
517
|
+
`ExtensionCommandContext` passed to command handlers.
|
|
518
|
+
- Extensions can set custom TUI components: editor, footer, header, widgets, working
|
|
519
|
+
indicator. (`pi-agent/extensions/types.d.ts:94-188`)
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
## 10. API Gaps
|
|
524
|
+
|
|
525
|
+
### What the brainstorm assumed PI has — that it does not
|
|
526
|
+
|
|
527
|
+
**PreToolUse hooks (absent).**
|
|
528
|
+
PI has no `PreToolUse` / `PostToolUse` hook system as found in Claude Code's
|
|
529
|
+
`~/.claude/settings.json#hooks`. The equivalent is registering a `tool_call` or
|
|
530
|
+
`tool_result` handler via `pi.on("tool_call", handler)`, which receives a mutable
|
|
531
|
+
`event.input` and can return `{ block: true, reason: "..." }` to halt execution.
|
|
532
|
+
(`pi-agent/extensions/types.d.ts:795, 705-709`) The mechanism exists but under different
|
|
533
|
+
names and a different programming model (in-process TypeScript callbacks, not external
|
|
534
|
+
shell processes).
|
|
535
|
+
|
|
536
|
+
**File-event hooks (absent).**
|
|
537
|
+
There are no per-file-operation hooks for Read, Write, Glob, or Grep. The `tool_call`
|
|
538
|
+
event is generic — it fires for all tools including `read`, `write`, `grep`, `find`, `ls`.
|
|
539
|
+
Type-narrowing via `isToolCallEventType("read", event)` is provided
|
|
540
|
+
(`pi-agent/extensions/types.d.ts:688-698`), but this is a `tool_call` handler with a type
|
|
541
|
+
check, not a dedicated file-event hook. No separate hook channels exist for these operations.
|
|
542
|
+
|
|
543
|
+
**`.pi-lens/` is a debug log directory, not a manifest directory.**
|
|
544
|
+
The path `~/.pi-lens/` (in user home) is written by `pi-lens/index.ts` as a debug log:
|
|
545
|
+
`const DEBUG_LOG_DIR = path.join(os.homedir(), ".pi-lens")` and
|
|
546
|
+
`const DEBUG_LOG = path.join(DEBUG_LOG_DIR, "sessionstart.log")`.
|
|
547
|
+
(`pi-lens/index.ts:47-48`) It contains `sessionstart.log` only. It is not a manifest
|
|
548
|
+
directory, not scanned for extensions or skills, and has no role in PI's extension
|
|
549
|
+
discovery system. The pi-lens package manifest is in its `package.json` under `"pi"`.
|
|
550
|
+
|
|
551
|
+
### What PI has that the brainstorm missed or underspecified
|
|
552
|
+
|
|
553
|
+
- **TUI customization API** — `ctx.ui.setEditorComponent()`, `setFooter()`, `setHeader()`,
|
|
554
|
+
`setWidget()`, `setWorkingIndicator()`, `custom()` for arbitrary keyboard-focused
|
|
555
|
+
components. (`pi-agent/extensions/types.d.ts:96-188`) This is richer than expected.
|
|
556
|
+
- **OAuth provider support** — `pi.registerProvider()` supports a full OAuth login/refresh
|
|
557
|
+
flow for enterprise SSO providers. (`pi-agent/extensions/types.d.ts:940-952`)
|
|
558
|
+
- **Session tree navigation from commands** — `ctx.fork()`, `ctx.navigateTree()`,
|
|
559
|
+
`ctx.newSession()`, `ctx.switchSession()` in `ExtensionCommandContext`.
|
|
560
|
+
(`pi-agent/extensions/types.d.ts:242-272`)
|
|
561
|
+
- **Context compaction control** — `session_before_compact` event with cancel/custom result;
|
|
562
|
+
`ctx.compact()` callable from any event handler. (`pi-agent/extensions/types.d.ts:775, 229-231`)
|
|
563
|
+
- **`resources_discover` event** — extensions can dynamically add skill/prompt/theme paths
|
|
564
|
+
per session. (`pi-agent/extensions/types.d.ts:771, 366-377`)
|
|
565
|
+
- **`before_agent_start` system prompt injection** — returning `{ systemPrompt }` from this
|
|
566
|
+
handler replaces the system prompt for that turn; multiple extensions chain.
|
|
567
|
+
(`pi-agent/extensions/types.d.ts:783, 720-726`)
|
|
568
|
+
- **Agent manager global registry** — `pi-subagents` exposes the manager via
|
|
569
|
+
`Symbol.for("pi-subagents:manager")` on `globalThis` for cross-package access without
|
|
570
|
+
going through RPC. (`pi-sub/index.ts:413-422`)
|
|
571
|
+
- **Batch notification / group join** — background agent completions are debounced and
|
|
572
|
+
grouped by `GroupJoinManager` before emitting UI notifications, with a 200 ms hold window.
|
|
573
|
+
(`pi-sub/index.ts:262-335`) This affects timing of `subagent-notification` custom messages
|
|
574
|
+
but not `subagents:completed` event timing.
|
package/docs/PORTING.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Porting Claude Code Skills and Agents to PI
|
|
2
|
+
|
|
3
|
+
This document maps Claude Code rnd-framework conventions to PI equivalents. Use it when porting skill `.md` files or agent `.md` files from a Claude Code plugin to PI, and when reviewing ported output to confirm no Claude-specific terms remain.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Frontmatter Translation
|
|
8
|
+
|
|
9
|
+
The table below covers every frontmatter key that appears in Claude Code skill and agent files. Keys marked **drop** have no PI equivalent and must be removed entirely.
|
|
10
|
+
|
|
11
|
+
| Claude Code | PI | Notes |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `name: rnd-planner` | drop | PI uses the filename stem as the agent/skill name |
|
|
14
|
+
| `description: ...` | `description: ...` | Identical; preserve verbatim |
|
|
15
|
+
| `tools: Read, Grep` | `tools: Read, Grep` | CSV string; unchanged |
|
|
16
|
+
| `disallowedTools: X,Y` | `disallowed_tools: X,Y` | Snake_case rename; same CSV value |
|
|
17
|
+
| `effort: low\|medium\|high` | `thinking: low\|medium\|high\|xhigh\|minimal` | Rename; `effort: low` → `thinking: minimal` is the closest match; see §4 for level semantics |
|
|
18
|
+
| `maxTurns: N` | `max_turns: N` | Snake_case rename; same integer value |
|
|
19
|
+
| `subagent_type: foo` | `type` | PI uses the filename stem as the implicit agent type. The spawn-time argument `type: 'rnd-builder'` (passed to `pi.events.emit('subagents:rpc:spawn', ...)`) matches the agent file's basename. |
|
|
20
|
+
| `color: "#3B82F6"` | drop | Not in PI custom-agent schema (PI-API.md §4) |
|
|
21
|
+
| `model: opus` | `model: claude-opus-4-7` | Use full PI-supported model ID |
|
|
22
|
+
| `prompt_mode: replace\|append` | `prompt_mode: replace\|append` | Identical; PI default is `replace` |
|
|
23
|
+
| `inherit_context: bool` | `inherit_context: bool` | Identical |
|
|
24
|
+
| `isolation: worktree` | `isolation: worktree` | Identical; PI-API.md §4 |
|
|
25
|
+
| `memory: user\|project\|local` | `memory: user\|project\|local` | Identical |
|
|
26
|
+
| `skills: [a,b,c]` | drop; use `inherit_skills: true` | PI has no named-skill list in agent frontmatter; rely on auto-injection via skill discovery (PI-API.md §7) |
|
|
27
|
+
| any unlisted key | drop | PI's agent parser ignores unknown keys, but remove to keep frontmatter canonical |
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Vocabulary Substitution
|
|
32
|
+
|
|
33
|
+
These are Claude Code constructs that appear in skill and agent body text. Replace each with the PI equivalent when porting.
|
|
34
|
+
|
|
35
|
+
| Claude Code | PI equivalent | Notes |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `Agent tool` | `subagents:rpc:spawn` event | The Claude Code `Agent` built-in tool has no PI counterpart; spawning is event-driven via `pi.events.emit('subagents:rpc:spawn', ...)` |
|
|
38
|
+
| `Agent({subagent_type, prompt})` | `pi.events.emit("subagents:rpc:spawn", {requestId, type, prompt, options})` | PI-API.md §2; reply on `subagents:rpc:spawn:reply:<requestId>` |
|
|
39
|
+
| `Skill tool` | auto-injected via skill discovery | Skills matching discovery paths are injected into the system prompt; explicit invocation via `/skill:<name>` when `disable-model-invocation: true` (PI-API.md §7) |
|
|
40
|
+
| `SendMessage` | not directly supported | Agent final result string is the primary inter-agent output; `subagents:completed` event payload `{id, result, ...}` carries it |
|
|
41
|
+
| `TaskCreate` / `TaskUpdate` / `TaskList` | `pi.appendEntry("rnd:phase"\|"rnd:wave"\|"rnd:verdict", data)` | PI-API.md §1; persists runtime state outside the LLM context window |
|
|
42
|
+
| `AskUserQuestion` | `ctx.ui.notify(text, level)` | No multi-option UI equivalent; the user's next turn carries their reply |
|
|
43
|
+
| `${CLAUDE_PLUGIN_ROOT}` | strip; use paths relative to extension root or resolve via `process.cwd()` | PI-API.md §1 |
|
|
44
|
+
| `CLAUDE_SESSION_ID` | strip | PI session ID is internal and not exposed to extension code |
|
|
45
|
+
| `~/.claude/agents/` | `.pi/agents/` (project) or `~/.pi/agent/agents/` (global) | See Install Locations below |
|
|
46
|
+
| `~/.claude/skills/` | `.pi/skills/` (project) or `~/.pi/agent/skills/` (global) | PI-API.md §7 |
|
|
47
|
+
| `${CLAUDE_PROJECT_DIR}` | `process.cwd()` | PI sets cwd to the project root before loading extensions |
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Install Locations
|
|
52
|
+
|
|
53
|
+
### Agents
|
|
54
|
+
|
|
55
|
+
Place ported `.md` files at `.pi/agents/<name>.md` (project-local, **primary**). This path is loaded on every agent invocation — no PI restart required.
|
|
56
|
+
|
|
57
|
+
Global fallback: `~/.pi/agent/agents/<name>.md`. Project-local overrides global on name collision.
|
|
58
|
+
|
|
59
|
+
The npm package `agents/` directory is **not** auto-discovered. PI's `loadCustomAgents` function hard-codes only the two paths above (`pi-sub/custom-agents.ts:20-28`). Users must manually copy files into one of the two paths; a `package.json` key analogous to `"pi": { "agents": [...] }` does not exist.
|
|
60
|
+
|
|
61
|
+
### Skills
|
|
62
|
+
|
|
63
|
+
PI auto-discovers skills from, in priority order (PI-API.md §7):
|
|
64
|
+
|
|
65
|
+
1. `~/.pi/agent/skills/` — global
|
|
66
|
+
2. `.pi/skills/` — project-local
|
|
67
|
+
3. npm packages declaring `"pi": { "skills": ["./skills"] }` in `package.json`
|
|
68
|
+
|
|
69
|
+
For npm-install distribution, add the following to `package.json`:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"pi": {
|
|
74
|
+
"skills": ["./skills"]
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Gate / Hook Translation
|
|
82
|
+
|
|
83
|
+
**Claude Code** uses external shell scripts listed in `hooks/hooks.json`. Each hook matches a lifecycle event (e.g., `PreToolUse`) with optional tool matchers (`Bash`, `Read`, etc.) and runs as a separate process.
|
|
84
|
+
|
|
85
|
+
**PI** uses a single in-process subscription:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
pi.on("tool_call", (event) => {
|
|
89
|
+
// return { block: true, reason: "..." } to veto; return void/null to allow
|
|
90
|
+
})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The handler receives the tool name and full input object. Returning `{ block: true, reason }` halts execution before the tool runs (PI-API.md §10; confirmed by static analysis of `ToolCallEventResult` at `dist/core/extensions/types.d.ts:705-709`). Multiple gates compose by funneling all logic through a single `tool_call` subscriber — not via separate hook registrations.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## What Does Not Port
|
|
98
|
+
|
|
99
|
+
These Claude Code constructs have no PI equivalent and must be removed entirely when porting:
|
|
100
|
+
|
|
101
|
+
- `${CLAUDE_PLUGIN_ROOT}` — no equivalent path variable in PI
|
|
102
|
+
- Multi-option `AskUserQuestion` UI — PI exposes only `ctx.ui.notify`; no structured choice dialogs
|
|
103
|
+
- `SendMessage` — there is no explicit agent-to-orchestrator messaging channel; communication flows through the `subagents:completed` result payload
|
|
104
|
+
- Dedicated `Agent` tool — PI spawning is event-driven via `subagents:rpc:spawn`, not a built-in LLM tool
|
|
105
|
+
- Separate hook event types (`PreToolUse`, `PostToolUse`, etc.) — PI has one `tool_call` event covering pre-execution only
|