@mariozechner/pi-coding-agent 0.25.3 → 0.26.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.
Files changed (125) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +47 -5
  3. package/dist/cli/session-picker.d.ts +2 -2
  4. package/dist/cli/session-picker.d.ts.map +1 -1
  5. package/dist/cli/session-picker.js +2 -2
  6. package/dist/cli/session-picker.js.map +1 -1
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +1 -13
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/compaction.d.ts.map +1 -1
  11. package/dist/core/compaction.js +1 -1
  12. package/dist/core/compaction.js.map +1 -1
  13. package/dist/core/custom-tools/loader.d.ts +3 -2
  14. package/dist/core/custom-tools/loader.d.ts.map +1 -1
  15. package/dist/core/custom-tools/loader.js +5 -4
  16. package/dist/core/custom-tools/loader.js.map +1 -1
  17. package/dist/core/export-html.d.ts.map +1 -1
  18. package/dist/core/export-html.js +1 -1
  19. package/dist/core/export-html.js.map +1 -1
  20. package/dist/core/hooks/loader.d.ts +2 -5
  21. package/dist/core/hooks/loader.d.ts.map +1 -1
  22. package/dist/core/hooks/loader.js +4 -7
  23. package/dist/core/hooks/loader.js.map +1 -1
  24. package/dist/core/messages.d.ts.map +1 -1
  25. package/dist/core/messages.js +1 -1
  26. package/dist/core/messages.js.map +1 -1
  27. package/dist/core/model-config.d.ts +5 -4
  28. package/dist/core/model-config.d.ts.map +1 -1
  29. package/dist/core/model-config.js +12 -19
  30. package/dist/core/model-config.js.map +1 -1
  31. package/dist/core/sdk.d.ts +211 -0
  32. package/dist/core/sdk.d.ts.map +1 -0
  33. package/dist/core/sdk.js +462 -0
  34. package/dist/core/sdk.js.map +1 -0
  35. package/dist/core/session-manager.d.ts +31 -91
  36. package/dist/core/session-manager.d.ts.map +1 -1
  37. package/dist/core/session-manager.js +188 -353
  38. package/dist/core/session-manager.js.map +1 -1
  39. package/dist/core/settings-manager.d.ts +12 -2
  40. package/dist/core/settings-manager.d.ts.map +1 -1
  41. package/dist/core/settings-manager.js +101 -37
  42. package/dist/core/settings-manager.js.map +1 -1
  43. package/dist/core/skills.d.ts +7 -1
  44. package/dist/core/skills.d.ts.map +1 -1
  45. package/dist/core/skills.js +7 -5
  46. package/dist/core/skills.js.map +1 -1
  47. package/dist/core/slash-commands.d.ts +9 -3
  48. package/dist/core/slash-commands.d.ts.map +1 -1
  49. package/dist/core/slash-commands.js +12 -9
  50. package/dist/core/slash-commands.js.map +1 -1
  51. package/dist/core/system-prompt.d.ts +24 -2
  52. package/dist/core/system-prompt.d.ts.map +1 -1
  53. package/dist/core/system-prompt.js +18 -16
  54. package/dist/core/system-prompt.js.map +1 -1
  55. package/dist/core/tools/grep.d.ts.map +1 -1
  56. package/dist/core/tools/grep.js +1 -1
  57. package/dist/core/tools/grep.js.map +1 -1
  58. package/dist/core/tools/index.d.ts +12 -22
  59. package/dist/core/tools/index.d.ts.map +1 -1
  60. package/dist/core/tools/index.js +2 -0
  61. package/dist/core/tools/index.js.map +1 -1
  62. package/dist/core/tools/read.d.ts.map +1 -1
  63. package/dist/core/tools/read.js +0 -1
  64. package/dist/core/tools/read.js.map +1 -1
  65. package/dist/core/tools/truncate.d.ts.map +1 -1
  66. package/dist/core/tools/truncate.js +1 -1
  67. package/dist/core/tools/truncate.js.map +1 -1
  68. package/dist/index.d.ts +2 -1
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +12 -0
  71. package/dist/index.js.map +1 -1
  72. package/dist/main.d.ts +4 -1
  73. package/dist/main.d.ts.map +1 -1
  74. package/dist/main.js +142 -312
  75. package/dist/main.js.map +1 -1
  76. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  77. package/dist/modes/interactive/components/armin.js +2 -2
  78. package/dist/modes/interactive/components/armin.js.map +1 -1
  79. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  80. package/dist/modes/interactive/components/bash-execution.js +3 -3
  81. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  82. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  83. package/dist/modes/interactive/components/footer.js +6 -6
  84. package/dist/modes/interactive/components/footer.js.map +1 -1
  85. package/dist/modes/interactive/components/hook-selector.d.ts.map +1 -1
  86. package/dist/modes/interactive/components/hook-selector.js +1 -1
  87. package/dist/modes/interactive/components/hook-selector.js.map +1 -1
  88. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  89. package/dist/modes/interactive/components/model-selector.js +2 -2
  90. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  91. package/dist/modes/interactive/components/session-selector.d.ts +3 -12
  92. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  93. package/dist/modes/interactive/components/session-selector.js +1 -3
  94. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  95. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  96. package/dist/modes/interactive/components/tool-execution.js +14 -14
  97. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  98. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  99. package/dist/modes/interactive/interactive-mode.js +7 -6
  100. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  101. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  102. package/dist/modes/interactive/theme/theme.js +10 -6
  103. package/dist/modes/interactive/theme/theme.js.map +1 -1
  104. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  105. package/dist/modes/rpc/rpc-client.js +1 -1
  106. package/dist/modes/rpc/rpc-client.js.map +1 -1
  107. package/dist/utils/shell.d.ts.map +1 -1
  108. package/dist/utils/shell.js +2 -2
  109. package/dist/utils/shell.js.map +1 -1
  110. package/docs/sdk.md +819 -0
  111. package/examples/README.md +29 -0
  112. package/examples/sdk/01-minimal.ts +22 -0
  113. package/examples/sdk/02-custom-model.ts +36 -0
  114. package/examples/sdk/03-custom-prompt.ts +44 -0
  115. package/examples/sdk/04-skills.ts +44 -0
  116. package/examples/sdk/05-tools.ts +67 -0
  117. package/examples/sdk/06-hooks.ts +61 -0
  118. package/examples/sdk/07-context-files.ts +36 -0
  119. package/examples/sdk/08-slash-commands.ts +37 -0
  120. package/examples/sdk/09-api-keys-and-oauth.ts +45 -0
  121. package/examples/sdk/10-settings.ts +38 -0
  122. package/examples/sdk/11-sessions.ts +46 -0
  123. package/examples/sdk/12-full-control.ts +91 -0
  124. package/examples/sdk/README.md +138 -0
  125. package/package.json +4 -4
package/docs/sdk.md ADDED
@@ -0,0 +1,819 @@
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, bash
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
+ > See [examples/sdk/05-tools.ts](../examples/sdk/05-tools.ts)
327
+
328
+ ### Custom Tools
329
+
330
+ ```typescript
331
+ import { Type } from "@sinclair/typebox";
332
+ import { createAgentSession, discoverCustomTools, type CustomAgentTool } from "@mariozechner/pi-coding-agent";
333
+
334
+ // Inline custom tool
335
+ const myTool: CustomAgentTool = {
336
+ name: "my_tool",
337
+ label: "My Tool",
338
+ description: "Does something useful",
339
+ parameters: Type.Object({
340
+ input: Type.String({ description: "Input value" }),
341
+ }),
342
+ execute: async (toolCallId, params) => ({
343
+ content: [{ type: "text", text: `Result: ${params.input}` }],
344
+ details: {},
345
+ }),
346
+ };
347
+
348
+ // Replace discovery with inline tools
349
+ const { session } = await createAgentSession({
350
+ customTools: [{ tool: myTool }],
351
+ });
352
+
353
+ // Merge with discovered tools
354
+ const discovered = await discoverCustomTools();
355
+ const { session } = await createAgentSession({
356
+ customTools: [...discovered, { tool: myTool }],
357
+ });
358
+
359
+ // Add paths without replacing discovery
360
+ const { session } = await createAgentSession({
361
+ additionalCustomToolPaths: ["/extra/tools"],
362
+ });
363
+ ```
364
+
365
+ > See [examples/sdk/05-tools.ts](../examples/sdk/05-tools.ts)
366
+
367
+ ### Hooks
368
+
369
+ ```typescript
370
+ import { createAgentSession, discoverHooks, type HookFactory } from "@mariozechner/pi-coding-agent";
371
+
372
+ // Inline hook
373
+ const loggingHook: HookFactory = (api) => {
374
+ api.on("tool_call", async (event) => {
375
+ console.log(`Tool: ${event.toolName}`);
376
+ return undefined; // Don't block
377
+ });
378
+
379
+ api.on("tool_call", async (event) => {
380
+ // Block dangerous commands
381
+ if (event.toolName === "bash" && event.input.command?.includes("rm -rf")) {
382
+ return { block: true, reason: "Dangerous command" };
383
+ }
384
+ return undefined;
385
+ });
386
+ };
387
+
388
+ // Replace discovery
389
+ const { session } = await createAgentSession({
390
+ hooks: [{ factory: loggingHook }],
391
+ });
392
+
393
+ // Disable all hooks
394
+ const { session } = await createAgentSession({
395
+ hooks: [],
396
+ });
397
+
398
+ // Merge with discovered
399
+ const discovered = await discoverHooks();
400
+ const { session } = await createAgentSession({
401
+ hooks: [...discovered, { factory: loggingHook }],
402
+ });
403
+
404
+ // Add paths without replacing
405
+ const { session } = await createAgentSession({
406
+ additionalHookPaths: ["/extra/hooks"],
407
+ });
408
+ ```
409
+
410
+ > See [examples/sdk/06-hooks.ts](../examples/sdk/06-hooks.ts)
411
+
412
+ ### Skills
413
+
414
+ ```typescript
415
+ import { createAgentSession, discoverSkills, type Skill } from "@mariozechner/pi-coding-agent";
416
+
417
+ // Discover and filter
418
+ const allSkills = discoverSkills();
419
+ const filtered = allSkills.filter(s => s.name.includes("search"));
420
+
421
+ // Custom skill
422
+ const mySkill: Skill = {
423
+ name: "my-skill",
424
+ description: "Custom instructions",
425
+ filePath: "/path/to/SKILL.md",
426
+ baseDir: "/path/to",
427
+ source: "custom",
428
+ };
429
+
430
+ const { session } = await createAgentSession({
431
+ skills: [...filtered, mySkill],
432
+ });
433
+
434
+ // Disable skills
435
+ const { session } = await createAgentSession({
436
+ skills: [],
437
+ });
438
+
439
+ // Discovery with settings filter
440
+ const skills = discoverSkills(process.cwd(), undefined, {
441
+ ignoredSkills: ["browser-*"], // glob patterns to exclude
442
+ includeSkills: ["search-*"], // glob patterns to include (empty = all)
443
+ });
444
+ ```
445
+
446
+ > See [examples/sdk/04-skills.ts](../examples/sdk/04-skills.ts)
447
+
448
+ ### Context Files
449
+
450
+ ```typescript
451
+ import { createAgentSession, discoverContextFiles } from "@mariozechner/pi-coding-agent";
452
+
453
+ // Discover AGENTS.md files
454
+ const discovered = discoverContextFiles();
455
+
456
+ // Add custom context
457
+ const { session } = await createAgentSession({
458
+ contextFiles: [
459
+ ...discovered,
460
+ {
461
+ path: "/virtual/AGENTS.md",
462
+ content: "# Guidelines\n\n- Be concise\n- Use TypeScript",
463
+ },
464
+ ],
465
+ });
466
+
467
+ // Disable context files
468
+ const { session } = await createAgentSession({
469
+ contextFiles: [],
470
+ });
471
+ ```
472
+
473
+ > See [examples/sdk/07-context-files.ts](../examples/sdk/07-context-files.ts)
474
+
475
+ ### Slash Commands
476
+
477
+ ```typescript
478
+ import { createAgentSession, discoverSlashCommands, type FileSlashCommand } from "@mariozechner/pi-coding-agent";
479
+
480
+ const discovered = discoverSlashCommands();
481
+
482
+ const customCommand: FileSlashCommand = {
483
+ name: "deploy",
484
+ description: "Deploy the application",
485
+ source: "(custom)",
486
+ content: "# Deploy\n\n1. Build\n2. Test\n3. Deploy",
487
+ };
488
+
489
+ const { session } = await createAgentSession({
490
+ slashCommands: [...discovered, customCommand],
491
+ });
492
+ ```
493
+
494
+ > See [examples/sdk/08-slash-commands.ts](../examples/sdk/08-slash-commands.ts)
495
+
496
+ ### Session Management
497
+
498
+ ```typescript
499
+ import { createAgentSession, SessionManager } from "@mariozechner/pi-coding-agent";
500
+
501
+ // In-memory (no persistence)
502
+ const { session } = await createAgentSession({
503
+ sessionManager: SessionManager.inMemory(),
504
+ });
505
+
506
+ // New persistent session
507
+ const { session } = await createAgentSession({
508
+ sessionManager: SessionManager.create(process.cwd()),
509
+ });
510
+
511
+ // Continue most recent
512
+ const { session, modelFallbackMessage } = await createAgentSession({
513
+ sessionManager: SessionManager.continueRecent(process.cwd()),
514
+ });
515
+ if (modelFallbackMessage) {
516
+ console.log("Note:", modelFallbackMessage);
517
+ }
518
+
519
+ // Open specific file
520
+ const { session } = await createAgentSession({
521
+ sessionManager: SessionManager.open("/path/to/session.jsonl"),
522
+ });
523
+
524
+ // List available sessions
525
+ const sessions = SessionManager.list(process.cwd());
526
+ for (const info of sessions) {
527
+ console.log(`${info.id}: ${info.firstMessage} (${info.messageCount} messages)`);
528
+ }
529
+
530
+ // Custom agentDir for sessions
531
+ const { session } = await createAgentSession({
532
+ agentDir: "/custom/agent",
533
+ sessionManager: SessionManager.create(process.cwd(), "/custom/agent"),
534
+ });
535
+ ```
536
+
537
+ > See [examples/sdk/11-sessions.ts](../examples/sdk/11-sessions.ts)
538
+
539
+ ### Settings Management
540
+
541
+ ```typescript
542
+ import { createAgentSession, SettingsManager, SessionManager } from "@mariozechner/pi-coding-agent";
543
+
544
+ // Default: loads from files (global + project merged)
545
+ const { session } = await createAgentSession({
546
+ settingsManager: SettingsManager.create(),
547
+ });
548
+
549
+ // With overrides
550
+ const settingsManager = SettingsManager.create();
551
+ settingsManager.applyOverrides({
552
+ compaction: { enabled: false },
553
+ retry: { enabled: true, maxRetries: 5 },
554
+ });
555
+ const { session } = await createAgentSession({ settingsManager });
556
+
557
+ // In-memory (no file I/O, for testing)
558
+ const { session } = await createAgentSession({
559
+ settingsManager: SettingsManager.inMemory({ compaction: { enabled: false } }),
560
+ sessionManager: SessionManager.inMemory(),
561
+ });
562
+
563
+ // Custom directories
564
+ const { session } = await createAgentSession({
565
+ settingsManager: SettingsManager.create("/custom/cwd", "/custom/agent"),
566
+ });
567
+ ```
568
+
569
+ **Static factories:**
570
+ - `SettingsManager.create(cwd?, agentDir?)` - Load from files
571
+ - `SettingsManager.inMemory(settings?)` - No file I/O
572
+
573
+ **Project-specific settings:**
574
+
575
+ Settings load from two locations and merge:
576
+ 1. Global: `~/.pi/agent/settings.json`
577
+ 2. Project: `<cwd>/.pi/settings.json`
578
+
579
+ Project overrides global. Nested objects merge keys. Setters only modify global (project is read-only for version control).
580
+
581
+ > See [examples/sdk/10-settings.ts](../examples/sdk/10-settings.ts)
582
+
583
+ ## Discovery Functions
584
+
585
+ All discovery functions accept optional `cwd` and `agentDir` parameters.
586
+
587
+ ```typescript
588
+ import {
589
+ discoverModels,
590
+ discoverAvailableModels,
591
+ findModel,
592
+ discoverSkills,
593
+ discoverHooks,
594
+ discoverCustomTools,
595
+ discoverContextFiles,
596
+ discoverSlashCommands,
597
+ loadSettings,
598
+ buildSystemPrompt,
599
+ } from "@mariozechner/pi-coding-agent";
600
+
601
+ // Models
602
+ const allModels = discoverModels();
603
+ const available = await discoverAvailableModels();
604
+ const { model, error } = findModel("anthropic", "claude-sonnet-4-20250514");
605
+
606
+ // Skills
607
+ const skills = discoverSkills(cwd, agentDir, skillsSettings);
608
+
609
+ // Hooks (async - loads TypeScript)
610
+ const hooks = await discoverHooks(cwd, agentDir);
611
+
612
+ // Custom tools (async - loads TypeScript)
613
+ const tools = await discoverCustomTools(cwd, agentDir);
614
+
615
+ // Context files
616
+ const contextFiles = discoverContextFiles(cwd, agentDir);
617
+
618
+ // Slash commands
619
+ const commands = discoverSlashCommands(cwd, agentDir);
620
+
621
+ // Settings (global + project merged)
622
+ const settings = loadSettings(cwd, agentDir);
623
+
624
+ // Build system prompt manually
625
+ const prompt = buildSystemPrompt({
626
+ skills,
627
+ contextFiles,
628
+ appendPrompt: "Additional instructions",
629
+ cwd,
630
+ });
631
+ ```
632
+
633
+ ## Return Value
634
+
635
+ `createAgentSession()` returns:
636
+
637
+ ```typescript
638
+ interface CreateAgentSessionResult {
639
+ // The session
640
+ session: AgentSession;
641
+
642
+ // Custom tools (for UI setup)
643
+ customToolsResult: {
644
+ tools: LoadedCustomTool[];
645
+ setUIContext: (ctx, hasUI) => void;
646
+ };
647
+
648
+ // Warning if session model couldn't be restored
649
+ modelFallbackMessage?: string;
650
+ }
651
+ ```
652
+
653
+ ## Complete Example
654
+
655
+ ```typescript
656
+ import { Type } from "@sinclair/typebox";
657
+ import {
658
+ createAgentSession,
659
+ configureOAuthStorage,
660
+ defaultGetApiKey,
661
+ findModel,
662
+ SessionManager,
663
+ SettingsManager,
664
+ readTool,
665
+ bashTool,
666
+ type HookFactory,
667
+ type CustomAgentTool,
668
+ } from "@mariozechner/pi-coding-agent";
669
+ import { getAgentDir } from "@mariozechner/pi-coding-agent/config";
670
+
671
+ // Use OAuth from default location
672
+ configureOAuthStorage(getAgentDir());
673
+
674
+ // Custom API key with fallback
675
+ const getApiKey = async (model: { provider: string }) => {
676
+ if (model.provider === "anthropic" && process.env.MY_KEY) {
677
+ return process.env.MY_KEY;
678
+ }
679
+ return defaultGetApiKey()(model as any);
680
+ };
681
+
682
+ // Inline hook
683
+ const auditHook: HookFactory = (api) => {
684
+ api.on("tool_call", async (event) => {
685
+ console.log(`[Audit] ${event.toolName}`);
686
+ return undefined;
687
+ });
688
+ };
689
+
690
+ // Inline tool
691
+ const statusTool: CustomAgentTool = {
692
+ name: "status",
693
+ label: "Status",
694
+ description: "Get system status",
695
+ parameters: Type.Object({}),
696
+ execute: async () => ({
697
+ content: [{ type: "text", text: `Uptime: ${process.uptime()}s` }],
698
+ details: {},
699
+ }),
700
+ };
701
+
702
+ const { model, error } = findModel("anthropic", "claude-sonnet-4-20250514");
703
+ if (error) throw new Error(error);
704
+ if (!model) throw new Error("Model not found");
705
+
706
+ // In-memory settings with overrides
707
+ const settingsManager = SettingsManager.inMemory({
708
+ compaction: { enabled: false },
709
+ retry: { enabled: true, maxRetries: 2 },
710
+ });
711
+
712
+ const { session } = await createAgentSession({
713
+ cwd: process.cwd(),
714
+ agentDir: "/custom/agent",
715
+
716
+ model,
717
+ thinkingLevel: "off",
718
+ getApiKey,
719
+
720
+ systemPrompt: "You are a minimal assistant. Be concise.",
721
+
722
+ tools: [readTool, bashTool],
723
+ customTools: [{ tool: statusTool }],
724
+ hooks: [{ factory: auditHook }],
725
+ skills: [],
726
+ contextFiles: [],
727
+ slashCommands: [],
728
+
729
+ sessionManager: SessionManager.inMemory(),
730
+ settingsManager,
731
+ });
732
+
733
+ session.subscribe((event) => {
734
+ if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
735
+ process.stdout.write(event.assistantMessageEvent.delta);
736
+ }
737
+ });
738
+
739
+ await session.prompt("Get status and list files.");
740
+ ```
741
+
742
+ ## RPC Mode Alternative
743
+
744
+ For subprocess-based integration, use RPC mode instead of the SDK:
745
+
746
+ ```bash
747
+ pi --mode rpc --no-session
748
+ ```
749
+
750
+ See [RPC documentation](rpc.md) for the JSON protocol.
751
+
752
+ The SDK is preferred when:
753
+ - You want type safety
754
+ - You're in the same Node.js process
755
+ - You need direct access to agent state
756
+ - You want to customize tools/hooks programmatically
757
+
758
+ RPC mode is preferred when:
759
+ - You're integrating from another language
760
+ - You want process isolation
761
+ - You're building a language-agnostic client
762
+
763
+ ## Exports
764
+
765
+ The main entry point exports:
766
+
767
+ ```typescript
768
+ // Factory
769
+ createAgentSession
770
+ configureOAuthStorage
771
+
772
+ // Discovery
773
+ discoverModels
774
+ discoverAvailableModels
775
+ findModel
776
+ discoverSkills
777
+ discoverHooks
778
+ discoverCustomTools
779
+ discoverContextFiles
780
+ discoverSlashCommands
781
+
782
+ // Helpers
783
+ defaultGetApiKey
784
+ loadSettings
785
+ buildSystemPrompt
786
+
787
+ // Session management
788
+ SessionManager
789
+ SettingsManager
790
+
791
+ // Built-in tools
792
+ codingTools
793
+ readOnlyTools
794
+ readTool, bashTool, editTool, writeTool
795
+ grepTool, findTool, lsTool
796
+
797
+ // Types
798
+ type CreateAgentSessionOptions
799
+ type CreateAgentSessionResult
800
+ type CustomAgentTool
801
+ type HookFactory
802
+ type Skill
803
+ type FileSlashCommand
804
+ type Settings
805
+ type SkillsSettings
806
+ type Tool
807
+ ```
808
+
809
+ For hook types, import from the hooks subpath:
810
+
811
+ ```typescript
812
+ import type { HookAPI, HookEvent, ToolCallEvent } from "@mariozechner/pi-coding-agent/hooks";
813
+ ```
814
+
815
+ For config utilities:
816
+
817
+ ```typescript
818
+ import { getAgentDir } from "@mariozechner/pi-coding-agent/config";
819
+ ```