@mariozechner/pi-coding-agent 0.25.4 → 0.26.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/CHANGELOG.md +18 -0
- package/README.md +47 -5
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +1 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/session-picker.d.ts +2 -2
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +2 -2
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -13
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/custom-tools/loader.d.ts +3 -2
- package/dist/core/custom-tools/loader.d.ts.map +1 -1
- package/dist/core/custom-tools/loader.js +5 -4
- package/dist/core/custom-tools/loader.js.map +1 -1
- package/dist/core/hooks/loader.d.ts +2 -5
- package/dist/core/hooks/loader.d.ts.map +1 -1
- package/dist/core/hooks/loader.js +4 -7
- package/dist/core/hooks/loader.js.map +1 -1
- package/dist/core/model-config.d.ts +5 -4
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +12 -19
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/sdk.d.ts +211 -0
- package/dist/core/sdk.d.ts.map +1 -0
- package/dist/core/sdk.js +466 -0
- package/dist/core/sdk.js.map +1 -0
- package/dist/core/session-manager.d.ts +31 -91
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +187 -352
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +12 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +101 -37
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +7 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +7 -5
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts +9 -3
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +10 -7
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts +24 -2
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +18 -16
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +6 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +149 -144
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts +7 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +105 -102
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts +7 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +128 -124
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +11 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +198 -194
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +31 -29
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +44 -16
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +6 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +90 -86
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/path-utils.d.ts +6 -1
- package/dist/core/tools/path-utils.d.ts.map +1 -1
- package/dist/core/tools/path-utils.js +17 -5
- package/dist/core/tools/path-utils.js.map +1 -1
- package/dist/core/tools/read.d.ts +7 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +118 -115
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts +6 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +63 -59
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +4 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +142 -312
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +3 -12
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +1 -3
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +3 -2
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +1 -1
- package/dist/utils/shell.js.map +1 -1
- package/docs/sdk.md +864 -0
- package/examples/README.md +29 -0
- package/examples/sdk/01-minimal.ts +22 -0
- package/examples/sdk/02-custom-model.ts +36 -0
- package/examples/sdk/03-custom-prompt.ts +44 -0
- package/examples/sdk/04-skills.ts +44 -0
- package/examples/sdk/05-tools.ts +93 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +36 -0
- package/examples/sdk/08-slash-commands.ts +37 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +45 -0
- package/examples/sdk/10-settings.ts +38 -0
- package/examples/sdk/11-sessions.ts +46 -0
- package/examples/sdk/12-full-control.ts +99 -0
- package/examples/sdk/README.md +138 -0
- package/package.json +4 -4
package/docs/sdk.md
ADDED
|
@@ -0,0 +1,864 @@
|
|
|
1
|
+
# SDK
|
|
2
|
+
|
|
3
|
+
The SDK provides programmatic access to pi's agent capabilities. Use it to embed pi in other applications, build custom interfaces, or integrate with automated workflows.
|
|
4
|
+
|
|
5
|
+
**Example use cases:**
|
|
6
|
+
- Build a custom UI (web, desktop, mobile)
|
|
7
|
+
- Integrate agent capabilities into existing applications
|
|
8
|
+
- Create automated pipelines with agent reasoning
|
|
9
|
+
- Build custom tools that spawn sub-agents
|
|
10
|
+
- Test agent behavior programmatically
|
|
11
|
+
|
|
12
|
+
See [examples/sdk/](../examples/sdk/) for working examples from minimal to full control.
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { createAgentSession, SessionManager } from "@mariozechner/pi-coding-agent";
|
|
18
|
+
|
|
19
|
+
const { session } = await createAgentSession({
|
|
20
|
+
sessionManager: SessionManager.inMemory(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
session.subscribe((event) => {
|
|
24
|
+
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
|
25
|
+
process.stdout.write(event.assistantMessageEvent.delta);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
await session.prompt("What files are in the current directory?");
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @mariozechner/pi-coding-agent
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The SDK is included in the main package. No separate installation needed.
|
|
39
|
+
|
|
40
|
+
## Core Concepts
|
|
41
|
+
|
|
42
|
+
### createAgentSession()
|
|
43
|
+
|
|
44
|
+
The main factory function. Creates an `AgentSession` with configurable options.
|
|
45
|
+
|
|
46
|
+
**Philosophy:** "Omit to discover, provide to override."
|
|
47
|
+
- Omit an option → pi discovers/loads from standard locations
|
|
48
|
+
- Provide an option → your value is used, discovery skipped for that option
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { createAgentSession } from "@mariozechner/pi-coding-agent";
|
|
52
|
+
|
|
53
|
+
// Minimal: all defaults (discovers everything from cwd and ~/.pi/agent)
|
|
54
|
+
const { session } = await createAgentSession();
|
|
55
|
+
|
|
56
|
+
// Custom: override specific options
|
|
57
|
+
const { session } = await createAgentSession({
|
|
58
|
+
model: myModel,
|
|
59
|
+
systemPrompt: "You are helpful.",
|
|
60
|
+
tools: [readTool, bashTool],
|
|
61
|
+
sessionManager: SessionManager.inMemory(),
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### AgentSession
|
|
66
|
+
|
|
67
|
+
The session manages the agent lifecycle, message history, and event streaming.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
interface AgentSession {
|
|
71
|
+
// Send a prompt and wait for completion
|
|
72
|
+
prompt(text: string, options?: PromptOptions): Promise<void>;
|
|
73
|
+
|
|
74
|
+
// Subscribe to events (returns unsubscribe function)
|
|
75
|
+
subscribe(listener: (event: AgentSessionEvent) => void): () => void;
|
|
76
|
+
|
|
77
|
+
// Session info
|
|
78
|
+
sessionFile: string | null;
|
|
79
|
+
sessionId: string;
|
|
80
|
+
|
|
81
|
+
// Model control
|
|
82
|
+
setModel(model: Model): Promise<void>;
|
|
83
|
+
setThinkingLevel(level: ThinkingLevel): void;
|
|
84
|
+
cycleModel(): Promise<ModelCycleResult | null>;
|
|
85
|
+
cycleThinkingLevel(): ThinkingLevel | null;
|
|
86
|
+
|
|
87
|
+
// State access
|
|
88
|
+
agent: Agent;
|
|
89
|
+
model: Model | null;
|
|
90
|
+
thinkingLevel: ThinkingLevel;
|
|
91
|
+
messages: AppMessage[];
|
|
92
|
+
isStreaming: boolean;
|
|
93
|
+
|
|
94
|
+
// Session management
|
|
95
|
+
reset(): Promise<void>;
|
|
96
|
+
branch(entryIndex: number): Promise<{ selectedText: string; skipped: boolean }>;
|
|
97
|
+
switchSession(sessionPath: string): Promise<void>;
|
|
98
|
+
|
|
99
|
+
// Compaction
|
|
100
|
+
compact(customInstructions?: string): Promise<CompactionResult>;
|
|
101
|
+
abortCompaction(): void;
|
|
102
|
+
|
|
103
|
+
// Abort current operation
|
|
104
|
+
abort(): Promise<void>;
|
|
105
|
+
|
|
106
|
+
// Cleanup
|
|
107
|
+
dispose(): void;
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Agent and AgentState
|
|
112
|
+
|
|
113
|
+
The `Agent` class (from `@mariozechner/pi-agent-core`) handles the core LLM interaction. Access it via `session.agent`.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// Access current state
|
|
117
|
+
const state = session.agent.state;
|
|
118
|
+
|
|
119
|
+
// state.messages: AppMessage[] - conversation history
|
|
120
|
+
// state.model: Model - current model
|
|
121
|
+
// state.thinkingLevel: ThinkingLevel - current thinking level
|
|
122
|
+
// state.systemPrompt: string - system prompt
|
|
123
|
+
// state.tools: Tool[] - available tools
|
|
124
|
+
|
|
125
|
+
// Replace messages (useful for branching, restoration)
|
|
126
|
+
session.agent.replaceMessages(messages);
|
|
127
|
+
|
|
128
|
+
// Wait for agent to finish processing
|
|
129
|
+
await session.agent.waitForIdle();
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Events
|
|
133
|
+
|
|
134
|
+
Subscribe to events to receive streaming output and lifecycle notifications.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
session.subscribe((event) => {
|
|
138
|
+
switch (event.type) {
|
|
139
|
+
// Streaming text from assistant
|
|
140
|
+
case "message_update":
|
|
141
|
+
if (event.assistantMessageEvent.type === "text_delta") {
|
|
142
|
+
process.stdout.write(event.assistantMessageEvent.delta);
|
|
143
|
+
}
|
|
144
|
+
if (event.assistantMessageEvent.type === "thinking_delta") {
|
|
145
|
+
// Thinking output (if thinking enabled)
|
|
146
|
+
}
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
// Tool execution
|
|
150
|
+
case "tool_execution_start":
|
|
151
|
+
console.log(`Tool: ${event.toolName}`);
|
|
152
|
+
break;
|
|
153
|
+
case "tool_execution_update":
|
|
154
|
+
// Streaming tool output
|
|
155
|
+
break;
|
|
156
|
+
case "tool_execution_end":
|
|
157
|
+
console.log(`Result: ${event.isError ? "error" : "success"}`);
|
|
158
|
+
break;
|
|
159
|
+
|
|
160
|
+
// Message lifecycle
|
|
161
|
+
case "message_start":
|
|
162
|
+
// New message starting
|
|
163
|
+
break;
|
|
164
|
+
case "message_end":
|
|
165
|
+
// Message complete
|
|
166
|
+
break;
|
|
167
|
+
|
|
168
|
+
// Agent lifecycle
|
|
169
|
+
case "agent_start":
|
|
170
|
+
// Agent started processing prompt
|
|
171
|
+
break;
|
|
172
|
+
case "agent_end":
|
|
173
|
+
// Agent finished (event.messages contains new messages)
|
|
174
|
+
break;
|
|
175
|
+
|
|
176
|
+
// Turn lifecycle (one LLM response + tool calls)
|
|
177
|
+
case "turn_start":
|
|
178
|
+
break;
|
|
179
|
+
case "turn_end":
|
|
180
|
+
// event.message: assistant response
|
|
181
|
+
// event.toolResults: tool results from this turn
|
|
182
|
+
break;
|
|
183
|
+
|
|
184
|
+
// Session events (auto-compaction, retry)
|
|
185
|
+
case "auto_compaction_start":
|
|
186
|
+
case "auto_compaction_end":
|
|
187
|
+
case "auto_retry_start":
|
|
188
|
+
case "auto_retry_end":
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Options Reference
|
|
195
|
+
|
|
196
|
+
### Directories
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
const { session } = await createAgentSession({
|
|
200
|
+
// Working directory for project-local discovery
|
|
201
|
+
cwd: process.cwd(), // default
|
|
202
|
+
|
|
203
|
+
// Global config directory
|
|
204
|
+
agentDir: "~/.pi/agent", // default (expands ~)
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`cwd` is used for:
|
|
209
|
+
- Project hooks (`.pi/hooks/`)
|
|
210
|
+
- Project tools (`.pi/tools/`)
|
|
211
|
+
- Project skills (`.pi/skills/`)
|
|
212
|
+
- Project commands (`.pi/commands/`)
|
|
213
|
+
- Context files (`AGENTS.md` walking up from cwd)
|
|
214
|
+
- Session directory naming
|
|
215
|
+
|
|
216
|
+
`agentDir` is used for:
|
|
217
|
+
- Global hooks (`hooks/`)
|
|
218
|
+
- Global tools (`tools/`)
|
|
219
|
+
- Global skills (`skills/`)
|
|
220
|
+
- Global commands (`commands/`)
|
|
221
|
+
- Global context file (`AGENTS.md`)
|
|
222
|
+
- Settings (`settings.json`)
|
|
223
|
+
- Models (`models.json`)
|
|
224
|
+
- OAuth tokens (`oauth.json`)
|
|
225
|
+
- Sessions (`sessions/`)
|
|
226
|
+
|
|
227
|
+
### Model
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import { findModel, discoverAvailableModels } from "@mariozechner/pi-coding-agent";
|
|
231
|
+
|
|
232
|
+
// Find specific model (returns { model, error })
|
|
233
|
+
const { model, error } = findModel("anthropic", "claude-sonnet-4-20250514");
|
|
234
|
+
if (error) throw new Error(error);
|
|
235
|
+
if (!model) throw new Error("Model not found");
|
|
236
|
+
|
|
237
|
+
// Or get all models with valid API keys
|
|
238
|
+
const available = await discoverAvailableModels();
|
|
239
|
+
|
|
240
|
+
const { session } = await createAgentSession({
|
|
241
|
+
model: model,
|
|
242
|
+
thinkingLevel: "medium", // off, minimal, low, medium, high, xhigh
|
|
243
|
+
|
|
244
|
+
// Models for cycling (Ctrl+P in interactive mode)
|
|
245
|
+
scopedModels: [
|
|
246
|
+
{ model: sonnet, thinkingLevel: "high" },
|
|
247
|
+
{ model: haiku, thinkingLevel: "off" },
|
|
248
|
+
],
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
If no model is provided:
|
|
253
|
+
1. Tries to restore from session (if continuing)
|
|
254
|
+
2. Uses default from settings
|
|
255
|
+
3. Falls back to first available model
|
|
256
|
+
|
|
257
|
+
> See [examples/sdk/02-custom-model.ts](../examples/sdk/02-custom-model.ts)
|
|
258
|
+
|
|
259
|
+
### API Keys
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
import { defaultGetApiKey, configureOAuthStorage } from "@mariozechner/pi-coding-agent";
|
|
263
|
+
|
|
264
|
+
// Default: checks models.json, OAuth, environment variables
|
|
265
|
+
const { session } = await createAgentSession();
|
|
266
|
+
|
|
267
|
+
// Custom resolver
|
|
268
|
+
const { session } = await createAgentSession({
|
|
269
|
+
getApiKey: async (model) => {
|
|
270
|
+
// Custom logic (secrets manager, database, etc.)
|
|
271
|
+
if (model.provider === "anthropic") {
|
|
272
|
+
return process.env.MY_ANTHROPIC_KEY;
|
|
273
|
+
}
|
|
274
|
+
// Fall back to default
|
|
275
|
+
return defaultGetApiKey()(model);
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Use OAuth from ~/.pi/agent with custom agentDir for everything else
|
|
280
|
+
configureOAuthStorage(); // Must call before createAgentSession
|
|
281
|
+
const { session } = await createAgentSession({
|
|
282
|
+
agentDir: "/custom/config",
|
|
283
|
+
// OAuth tokens still come from ~/.pi/agent/oauth.json
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
> See [examples/sdk/09-api-keys-and-oauth.ts](../examples/sdk/09-api-keys-and-oauth.ts)
|
|
288
|
+
|
|
289
|
+
### System Prompt
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
const { session } = await createAgentSession({
|
|
293
|
+
// Replace entirely
|
|
294
|
+
systemPrompt: "You are a helpful assistant.",
|
|
295
|
+
|
|
296
|
+
// Or modify default (receives default, returns modified)
|
|
297
|
+
systemPrompt: (defaultPrompt) => {
|
|
298
|
+
return `${defaultPrompt}\n\n## Additional Rules\n- Be concise`;
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
> See [examples/sdk/03-custom-prompt.ts](../examples/sdk/03-custom-prompt.ts)
|
|
304
|
+
|
|
305
|
+
### Tools
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
import {
|
|
309
|
+
codingTools, // read, bash, edit, write (default)
|
|
310
|
+
readOnlyTools, // read, grep, find, ls
|
|
311
|
+
readTool, bashTool, editTool, writeTool,
|
|
312
|
+
grepTool, findTool, lsTool,
|
|
313
|
+
} from "@mariozechner/pi-coding-agent";
|
|
314
|
+
|
|
315
|
+
// Use built-in tool set
|
|
316
|
+
const { session } = await createAgentSession({
|
|
317
|
+
tools: readOnlyTools,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Pick specific tools
|
|
321
|
+
const { session } = await createAgentSession({
|
|
322
|
+
tools: [readTool, bashTool, grepTool],
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
#### Tools with Custom cwd
|
|
327
|
+
|
|
328
|
+
**Important:** The pre-built tool instances (`readTool`, `bashTool`, etc.) use `process.cwd()` for path resolution. When you specify a custom `cwd` AND provide explicit `tools`, you must use the tool factory functions to ensure paths resolve correctly:
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
import {
|
|
332
|
+
createCodingTools, // Creates [read, bash, edit, write] for specific cwd
|
|
333
|
+
createReadOnlyTools, // Creates [read, grep, find, ls] for specific cwd
|
|
334
|
+
createReadTool,
|
|
335
|
+
createBashTool,
|
|
336
|
+
createEditTool,
|
|
337
|
+
createWriteTool,
|
|
338
|
+
createGrepTool,
|
|
339
|
+
createFindTool,
|
|
340
|
+
createLsTool,
|
|
341
|
+
} from "@mariozechner/pi-coding-agent";
|
|
342
|
+
|
|
343
|
+
const cwd = "/path/to/project";
|
|
344
|
+
|
|
345
|
+
// Use factory for tool sets
|
|
346
|
+
const { session } = await createAgentSession({
|
|
347
|
+
cwd,
|
|
348
|
+
tools: createCodingTools(cwd), // Tools resolve paths relative to cwd
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// Or pick specific tools
|
|
352
|
+
const { session } = await createAgentSession({
|
|
353
|
+
cwd,
|
|
354
|
+
tools: [createReadTool(cwd), createBashTool(cwd), createGrepTool(cwd)],
|
|
355
|
+
});
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**When you don't need factories:**
|
|
359
|
+
- If you omit `tools`, pi automatically creates them with the correct `cwd`
|
|
360
|
+
- If you use `process.cwd()` as your `cwd`, the pre-built instances work fine
|
|
361
|
+
|
|
362
|
+
**When you must use factories:**
|
|
363
|
+
- When you specify both `cwd` (different from `process.cwd()`) AND `tools`
|
|
364
|
+
|
|
365
|
+
> See [examples/sdk/05-tools.ts](../examples/sdk/05-tools.ts)
|
|
366
|
+
|
|
367
|
+
### Custom Tools
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
import { Type } from "@sinclair/typebox";
|
|
371
|
+
import { createAgentSession, discoverCustomTools, type CustomAgentTool } from "@mariozechner/pi-coding-agent";
|
|
372
|
+
|
|
373
|
+
// Inline custom tool
|
|
374
|
+
const myTool: CustomAgentTool = {
|
|
375
|
+
name: "my_tool",
|
|
376
|
+
label: "My Tool",
|
|
377
|
+
description: "Does something useful",
|
|
378
|
+
parameters: Type.Object({
|
|
379
|
+
input: Type.String({ description: "Input value" }),
|
|
380
|
+
}),
|
|
381
|
+
execute: async (toolCallId, params) => ({
|
|
382
|
+
content: [{ type: "text", text: `Result: ${params.input}` }],
|
|
383
|
+
details: {},
|
|
384
|
+
}),
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// Replace discovery with inline tools
|
|
388
|
+
const { session } = await createAgentSession({
|
|
389
|
+
customTools: [{ tool: myTool }],
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// Merge with discovered tools
|
|
393
|
+
const discovered = await discoverCustomTools();
|
|
394
|
+
const { session } = await createAgentSession({
|
|
395
|
+
customTools: [...discovered, { tool: myTool }],
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// Add paths without replacing discovery
|
|
399
|
+
const { session } = await createAgentSession({
|
|
400
|
+
additionalCustomToolPaths: ["/extra/tools"],
|
|
401
|
+
});
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
> See [examples/sdk/05-tools.ts](../examples/sdk/05-tools.ts)
|
|
405
|
+
|
|
406
|
+
### Hooks
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
import { createAgentSession, discoverHooks, type HookFactory } from "@mariozechner/pi-coding-agent";
|
|
410
|
+
|
|
411
|
+
// Inline hook
|
|
412
|
+
const loggingHook: HookFactory = (api) => {
|
|
413
|
+
api.on("tool_call", async (event) => {
|
|
414
|
+
console.log(`Tool: ${event.toolName}`);
|
|
415
|
+
return undefined; // Don't block
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
api.on("tool_call", async (event) => {
|
|
419
|
+
// Block dangerous commands
|
|
420
|
+
if (event.toolName === "bash" && event.input.command?.includes("rm -rf")) {
|
|
421
|
+
return { block: true, reason: "Dangerous command" };
|
|
422
|
+
}
|
|
423
|
+
return undefined;
|
|
424
|
+
});
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// Replace discovery
|
|
428
|
+
const { session } = await createAgentSession({
|
|
429
|
+
hooks: [{ factory: loggingHook }],
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// Disable all hooks
|
|
433
|
+
const { session } = await createAgentSession({
|
|
434
|
+
hooks: [],
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// Merge with discovered
|
|
438
|
+
const discovered = await discoverHooks();
|
|
439
|
+
const { session } = await createAgentSession({
|
|
440
|
+
hooks: [...discovered, { factory: loggingHook }],
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
// Add paths without replacing
|
|
444
|
+
const { session } = await createAgentSession({
|
|
445
|
+
additionalHookPaths: ["/extra/hooks"],
|
|
446
|
+
});
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
> See [examples/sdk/06-hooks.ts](../examples/sdk/06-hooks.ts)
|
|
450
|
+
|
|
451
|
+
### Skills
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
import { createAgentSession, discoverSkills, type Skill } from "@mariozechner/pi-coding-agent";
|
|
455
|
+
|
|
456
|
+
// Discover and filter
|
|
457
|
+
const allSkills = discoverSkills();
|
|
458
|
+
const filtered = allSkills.filter(s => s.name.includes("search"));
|
|
459
|
+
|
|
460
|
+
// Custom skill
|
|
461
|
+
const mySkill: Skill = {
|
|
462
|
+
name: "my-skill",
|
|
463
|
+
description: "Custom instructions",
|
|
464
|
+
filePath: "/path/to/SKILL.md",
|
|
465
|
+
baseDir: "/path/to",
|
|
466
|
+
source: "custom",
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
const { session } = await createAgentSession({
|
|
470
|
+
skills: [...filtered, mySkill],
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// Disable skills
|
|
474
|
+
const { session } = await createAgentSession({
|
|
475
|
+
skills: [],
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
// Discovery with settings filter
|
|
479
|
+
const skills = discoverSkills(process.cwd(), undefined, {
|
|
480
|
+
ignoredSkills: ["browser-*"], // glob patterns to exclude
|
|
481
|
+
includeSkills: ["search-*"], // glob patterns to include (empty = all)
|
|
482
|
+
});
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
> See [examples/sdk/04-skills.ts](../examples/sdk/04-skills.ts)
|
|
486
|
+
|
|
487
|
+
### Context Files
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
import { createAgentSession, discoverContextFiles } from "@mariozechner/pi-coding-agent";
|
|
491
|
+
|
|
492
|
+
// Discover AGENTS.md files
|
|
493
|
+
const discovered = discoverContextFiles();
|
|
494
|
+
|
|
495
|
+
// Add custom context
|
|
496
|
+
const { session } = await createAgentSession({
|
|
497
|
+
contextFiles: [
|
|
498
|
+
...discovered,
|
|
499
|
+
{
|
|
500
|
+
path: "/virtual/AGENTS.md",
|
|
501
|
+
content: "# Guidelines\n\n- Be concise\n- Use TypeScript",
|
|
502
|
+
},
|
|
503
|
+
],
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
// Disable context files
|
|
507
|
+
const { session } = await createAgentSession({
|
|
508
|
+
contextFiles: [],
|
|
509
|
+
});
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
> See [examples/sdk/07-context-files.ts](../examples/sdk/07-context-files.ts)
|
|
513
|
+
|
|
514
|
+
### Slash Commands
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
import { createAgentSession, discoverSlashCommands, type FileSlashCommand } from "@mariozechner/pi-coding-agent";
|
|
518
|
+
|
|
519
|
+
const discovered = discoverSlashCommands();
|
|
520
|
+
|
|
521
|
+
const customCommand: FileSlashCommand = {
|
|
522
|
+
name: "deploy",
|
|
523
|
+
description: "Deploy the application",
|
|
524
|
+
source: "(custom)",
|
|
525
|
+
content: "# Deploy\n\n1. Build\n2. Test\n3. Deploy",
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
const { session } = await createAgentSession({
|
|
529
|
+
slashCommands: [...discovered, customCommand],
|
|
530
|
+
});
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
> See [examples/sdk/08-slash-commands.ts](../examples/sdk/08-slash-commands.ts)
|
|
534
|
+
|
|
535
|
+
### Session Management
|
|
536
|
+
|
|
537
|
+
```typescript
|
|
538
|
+
import { createAgentSession, SessionManager } from "@mariozechner/pi-coding-agent";
|
|
539
|
+
|
|
540
|
+
// In-memory (no persistence)
|
|
541
|
+
const { session } = await createAgentSession({
|
|
542
|
+
sessionManager: SessionManager.inMemory(),
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
// New persistent session
|
|
546
|
+
const { session } = await createAgentSession({
|
|
547
|
+
sessionManager: SessionManager.create(process.cwd()),
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// Continue most recent
|
|
551
|
+
const { session, modelFallbackMessage } = await createAgentSession({
|
|
552
|
+
sessionManager: SessionManager.continueRecent(process.cwd()),
|
|
553
|
+
});
|
|
554
|
+
if (modelFallbackMessage) {
|
|
555
|
+
console.log("Note:", modelFallbackMessage);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Open specific file
|
|
559
|
+
const { session } = await createAgentSession({
|
|
560
|
+
sessionManager: SessionManager.open("/path/to/session.jsonl"),
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
// List available sessions
|
|
564
|
+
const sessions = SessionManager.list(process.cwd());
|
|
565
|
+
for (const info of sessions) {
|
|
566
|
+
console.log(`${info.id}: ${info.firstMessage} (${info.messageCount} messages)`);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Custom agentDir for sessions
|
|
570
|
+
const { session } = await createAgentSession({
|
|
571
|
+
agentDir: "/custom/agent",
|
|
572
|
+
sessionManager: SessionManager.create(process.cwd(), "/custom/agent"),
|
|
573
|
+
});
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
> See [examples/sdk/11-sessions.ts](../examples/sdk/11-sessions.ts)
|
|
577
|
+
|
|
578
|
+
### Settings Management
|
|
579
|
+
|
|
580
|
+
```typescript
|
|
581
|
+
import { createAgentSession, SettingsManager, SessionManager } from "@mariozechner/pi-coding-agent";
|
|
582
|
+
|
|
583
|
+
// Default: loads from files (global + project merged)
|
|
584
|
+
const { session } = await createAgentSession({
|
|
585
|
+
settingsManager: SettingsManager.create(),
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
// With overrides
|
|
589
|
+
const settingsManager = SettingsManager.create();
|
|
590
|
+
settingsManager.applyOverrides({
|
|
591
|
+
compaction: { enabled: false },
|
|
592
|
+
retry: { enabled: true, maxRetries: 5 },
|
|
593
|
+
});
|
|
594
|
+
const { session } = await createAgentSession({ settingsManager });
|
|
595
|
+
|
|
596
|
+
// In-memory (no file I/O, for testing)
|
|
597
|
+
const { session } = await createAgentSession({
|
|
598
|
+
settingsManager: SettingsManager.inMemory({ compaction: { enabled: false } }),
|
|
599
|
+
sessionManager: SessionManager.inMemory(),
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
// Custom directories
|
|
603
|
+
const { session } = await createAgentSession({
|
|
604
|
+
settingsManager: SettingsManager.create("/custom/cwd", "/custom/agent"),
|
|
605
|
+
});
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
**Static factories:**
|
|
609
|
+
- `SettingsManager.create(cwd?, agentDir?)` - Load from files
|
|
610
|
+
- `SettingsManager.inMemory(settings?)` - No file I/O
|
|
611
|
+
|
|
612
|
+
**Project-specific settings:**
|
|
613
|
+
|
|
614
|
+
Settings load from two locations and merge:
|
|
615
|
+
1. Global: `~/.pi/agent/settings.json`
|
|
616
|
+
2. Project: `<cwd>/.pi/settings.json`
|
|
617
|
+
|
|
618
|
+
Project overrides global. Nested objects merge keys. Setters only modify global (project is read-only for version control).
|
|
619
|
+
|
|
620
|
+
> See [examples/sdk/10-settings.ts](../examples/sdk/10-settings.ts)
|
|
621
|
+
|
|
622
|
+
## Discovery Functions
|
|
623
|
+
|
|
624
|
+
All discovery functions accept optional `cwd` and `agentDir` parameters.
|
|
625
|
+
|
|
626
|
+
```typescript
|
|
627
|
+
import {
|
|
628
|
+
discoverModels,
|
|
629
|
+
discoverAvailableModels,
|
|
630
|
+
findModel,
|
|
631
|
+
discoverSkills,
|
|
632
|
+
discoverHooks,
|
|
633
|
+
discoverCustomTools,
|
|
634
|
+
discoverContextFiles,
|
|
635
|
+
discoverSlashCommands,
|
|
636
|
+
loadSettings,
|
|
637
|
+
buildSystemPrompt,
|
|
638
|
+
} from "@mariozechner/pi-coding-agent";
|
|
639
|
+
|
|
640
|
+
// Models
|
|
641
|
+
const allModels = discoverModels();
|
|
642
|
+
const available = await discoverAvailableModels();
|
|
643
|
+
const { model, error } = findModel("anthropic", "claude-sonnet-4-20250514");
|
|
644
|
+
|
|
645
|
+
// Skills
|
|
646
|
+
const skills = discoverSkills(cwd, agentDir, skillsSettings);
|
|
647
|
+
|
|
648
|
+
// Hooks (async - loads TypeScript)
|
|
649
|
+
const hooks = await discoverHooks(cwd, agentDir);
|
|
650
|
+
|
|
651
|
+
// Custom tools (async - loads TypeScript)
|
|
652
|
+
const tools = await discoverCustomTools(cwd, agentDir);
|
|
653
|
+
|
|
654
|
+
// Context files
|
|
655
|
+
const contextFiles = discoverContextFiles(cwd, agentDir);
|
|
656
|
+
|
|
657
|
+
// Slash commands
|
|
658
|
+
const commands = discoverSlashCommands(cwd, agentDir);
|
|
659
|
+
|
|
660
|
+
// Settings (global + project merged)
|
|
661
|
+
const settings = loadSettings(cwd, agentDir);
|
|
662
|
+
|
|
663
|
+
// Build system prompt manually
|
|
664
|
+
const prompt = buildSystemPrompt({
|
|
665
|
+
skills,
|
|
666
|
+
contextFiles,
|
|
667
|
+
appendPrompt: "Additional instructions",
|
|
668
|
+
cwd,
|
|
669
|
+
});
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
## Return Value
|
|
673
|
+
|
|
674
|
+
`createAgentSession()` returns:
|
|
675
|
+
|
|
676
|
+
```typescript
|
|
677
|
+
interface CreateAgentSessionResult {
|
|
678
|
+
// The session
|
|
679
|
+
session: AgentSession;
|
|
680
|
+
|
|
681
|
+
// Custom tools (for UI setup)
|
|
682
|
+
customToolsResult: {
|
|
683
|
+
tools: LoadedCustomTool[];
|
|
684
|
+
setUIContext: (ctx, hasUI) => void;
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
// Warning if session model couldn't be restored
|
|
688
|
+
modelFallbackMessage?: string;
|
|
689
|
+
}
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
## Complete Example
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
import { Type } from "@sinclair/typebox";
|
|
696
|
+
import {
|
|
697
|
+
createAgentSession,
|
|
698
|
+
configureOAuthStorage,
|
|
699
|
+
defaultGetApiKey,
|
|
700
|
+
findModel,
|
|
701
|
+
SessionManager,
|
|
702
|
+
SettingsManager,
|
|
703
|
+
readTool,
|
|
704
|
+
bashTool,
|
|
705
|
+
type HookFactory,
|
|
706
|
+
type CustomAgentTool,
|
|
707
|
+
} from "@mariozechner/pi-coding-agent";
|
|
708
|
+
import { getAgentDir } from "@mariozechner/pi-coding-agent/config";
|
|
709
|
+
|
|
710
|
+
// Use OAuth from default location
|
|
711
|
+
configureOAuthStorage(getAgentDir());
|
|
712
|
+
|
|
713
|
+
// Custom API key with fallback
|
|
714
|
+
const getApiKey = async (model: { provider: string }) => {
|
|
715
|
+
if (model.provider === "anthropic" && process.env.MY_KEY) {
|
|
716
|
+
return process.env.MY_KEY;
|
|
717
|
+
}
|
|
718
|
+
return defaultGetApiKey()(model as any);
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
// Inline hook
|
|
722
|
+
const auditHook: HookFactory = (api) => {
|
|
723
|
+
api.on("tool_call", async (event) => {
|
|
724
|
+
console.log(`[Audit] ${event.toolName}`);
|
|
725
|
+
return undefined;
|
|
726
|
+
});
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
// Inline tool
|
|
730
|
+
const statusTool: CustomAgentTool = {
|
|
731
|
+
name: "status",
|
|
732
|
+
label: "Status",
|
|
733
|
+
description: "Get system status",
|
|
734
|
+
parameters: Type.Object({}),
|
|
735
|
+
execute: async () => ({
|
|
736
|
+
content: [{ type: "text", text: `Uptime: ${process.uptime()}s` }],
|
|
737
|
+
details: {},
|
|
738
|
+
}),
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
const { model, error } = findModel("anthropic", "claude-sonnet-4-20250514");
|
|
742
|
+
if (error) throw new Error(error);
|
|
743
|
+
if (!model) throw new Error("Model not found");
|
|
744
|
+
|
|
745
|
+
// In-memory settings with overrides
|
|
746
|
+
const settingsManager = SettingsManager.inMemory({
|
|
747
|
+
compaction: { enabled: false },
|
|
748
|
+
retry: { enabled: true, maxRetries: 2 },
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
const { session } = await createAgentSession({
|
|
752
|
+
cwd: process.cwd(),
|
|
753
|
+
agentDir: "/custom/agent",
|
|
754
|
+
|
|
755
|
+
model,
|
|
756
|
+
thinkingLevel: "off",
|
|
757
|
+
getApiKey,
|
|
758
|
+
|
|
759
|
+
systemPrompt: "You are a minimal assistant. Be concise.",
|
|
760
|
+
|
|
761
|
+
tools: [readTool, bashTool],
|
|
762
|
+
customTools: [{ tool: statusTool }],
|
|
763
|
+
hooks: [{ factory: auditHook }],
|
|
764
|
+
skills: [],
|
|
765
|
+
contextFiles: [],
|
|
766
|
+
slashCommands: [],
|
|
767
|
+
|
|
768
|
+
sessionManager: SessionManager.inMemory(),
|
|
769
|
+
settingsManager,
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
session.subscribe((event) => {
|
|
773
|
+
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
|
774
|
+
process.stdout.write(event.assistantMessageEvent.delta);
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
await session.prompt("Get status and list files.");
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
## RPC Mode Alternative
|
|
782
|
+
|
|
783
|
+
For subprocess-based integration, use RPC mode instead of the SDK:
|
|
784
|
+
|
|
785
|
+
```bash
|
|
786
|
+
pi --mode rpc --no-session
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
See [RPC documentation](rpc.md) for the JSON protocol.
|
|
790
|
+
|
|
791
|
+
The SDK is preferred when:
|
|
792
|
+
- You want type safety
|
|
793
|
+
- You're in the same Node.js process
|
|
794
|
+
- You need direct access to agent state
|
|
795
|
+
- You want to customize tools/hooks programmatically
|
|
796
|
+
|
|
797
|
+
RPC mode is preferred when:
|
|
798
|
+
- You're integrating from another language
|
|
799
|
+
- You want process isolation
|
|
800
|
+
- You're building a language-agnostic client
|
|
801
|
+
|
|
802
|
+
## Exports
|
|
803
|
+
|
|
804
|
+
The main entry point exports:
|
|
805
|
+
|
|
806
|
+
```typescript
|
|
807
|
+
// Factory
|
|
808
|
+
createAgentSession
|
|
809
|
+
configureOAuthStorage
|
|
810
|
+
|
|
811
|
+
// Discovery
|
|
812
|
+
discoverModels
|
|
813
|
+
discoverAvailableModels
|
|
814
|
+
findModel
|
|
815
|
+
discoverSkills
|
|
816
|
+
discoverHooks
|
|
817
|
+
discoverCustomTools
|
|
818
|
+
discoverContextFiles
|
|
819
|
+
discoverSlashCommands
|
|
820
|
+
|
|
821
|
+
// Helpers
|
|
822
|
+
defaultGetApiKey
|
|
823
|
+
loadSettings
|
|
824
|
+
buildSystemPrompt
|
|
825
|
+
|
|
826
|
+
// Session management
|
|
827
|
+
SessionManager
|
|
828
|
+
SettingsManager
|
|
829
|
+
|
|
830
|
+
// Built-in tools (use process.cwd())
|
|
831
|
+
codingTools
|
|
832
|
+
readOnlyTools
|
|
833
|
+
readTool, bashTool, editTool, writeTool
|
|
834
|
+
grepTool, findTool, lsTool
|
|
835
|
+
|
|
836
|
+
// Tool factories (for custom cwd)
|
|
837
|
+
createCodingTools
|
|
838
|
+
createReadOnlyTools
|
|
839
|
+
createReadTool, createBashTool, createEditTool, createWriteTool
|
|
840
|
+
createGrepTool, createFindTool, createLsTool
|
|
841
|
+
|
|
842
|
+
// Types
|
|
843
|
+
type CreateAgentSessionOptions
|
|
844
|
+
type CreateAgentSessionResult
|
|
845
|
+
type CustomAgentTool
|
|
846
|
+
type HookFactory
|
|
847
|
+
type Skill
|
|
848
|
+
type FileSlashCommand
|
|
849
|
+
type Settings
|
|
850
|
+
type SkillsSettings
|
|
851
|
+
type Tool
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
For hook types, import from the hooks subpath:
|
|
855
|
+
|
|
856
|
+
```typescript
|
|
857
|
+
import type { HookAPI, HookEvent, ToolCallEvent } from "@mariozechner/pi-coding-agent/hooks";
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
For config utilities:
|
|
861
|
+
|
|
862
|
+
```typescript
|
|
863
|
+
import { getAgentDir } from "@mariozechner/pi-coding-agent/config";
|
|
864
|
+
```
|