@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.
Files changed (118) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +47 -5
  3. package/dist/cli/file-processor.d.ts.map +1 -1
  4. package/dist/cli/file-processor.js +1 -1
  5. package/dist/cli/file-processor.js.map +1 -1
  6. package/dist/cli/session-picker.d.ts +2 -2
  7. package/dist/cli/session-picker.d.ts.map +1 -1
  8. package/dist/cli/session-picker.js +2 -2
  9. package/dist/cli/session-picker.js.map +1 -1
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +1 -13
  12. package/dist/core/agent-session.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/hooks/loader.d.ts +2 -5
  18. package/dist/core/hooks/loader.d.ts.map +1 -1
  19. package/dist/core/hooks/loader.js +4 -7
  20. package/dist/core/hooks/loader.js.map +1 -1
  21. package/dist/core/model-config.d.ts +5 -4
  22. package/dist/core/model-config.d.ts.map +1 -1
  23. package/dist/core/model-config.js +12 -19
  24. package/dist/core/model-config.js.map +1 -1
  25. package/dist/core/sdk.d.ts +211 -0
  26. package/dist/core/sdk.d.ts.map +1 -0
  27. package/dist/core/sdk.js +466 -0
  28. package/dist/core/sdk.js.map +1 -0
  29. package/dist/core/session-manager.d.ts +31 -91
  30. package/dist/core/session-manager.d.ts.map +1 -1
  31. package/dist/core/session-manager.js +187 -352
  32. package/dist/core/session-manager.js.map +1 -1
  33. package/dist/core/settings-manager.d.ts +12 -2
  34. package/dist/core/settings-manager.d.ts.map +1 -1
  35. package/dist/core/settings-manager.js +101 -37
  36. package/dist/core/settings-manager.js.map +1 -1
  37. package/dist/core/skills.d.ts +7 -1
  38. package/dist/core/skills.d.ts.map +1 -1
  39. package/dist/core/skills.js +7 -5
  40. package/dist/core/skills.js.map +1 -1
  41. package/dist/core/slash-commands.d.ts +9 -3
  42. package/dist/core/slash-commands.d.ts.map +1 -1
  43. package/dist/core/slash-commands.js +10 -7
  44. package/dist/core/slash-commands.js.map +1 -1
  45. package/dist/core/system-prompt.d.ts +24 -2
  46. package/dist/core/system-prompt.d.ts.map +1 -1
  47. package/dist/core/system-prompt.js +18 -16
  48. package/dist/core/system-prompt.js.map +1 -1
  49. package/dist/core/tools/bash.d.ts +6 -1
  50. package/dist/core/tools/bash.d.ts.map +1 -1
  51. package/dist/core/tools/bash.js +149 -144
  52. package/dist/core/tools/bash.js.map +1 -1
  53. package/dist/core/tools/edit.d.ts +7 -1
  54. package/dist/core/tools/edit.d.ts.map +1 -1
  55. package/dist/core/tools/edit.js +105 -102
  56. package/dist/core/tools/edit.js.map +1 -1
  57. package/dist/core/tools/find.d.ts +7 -1
  58. package/dist/core/tools/find.d.ts.map +1 -1
  59. package/dist/core/tools/find.js +128 -124
  60. package/dist/core/tools/find.js.map +1 -1
  61. package/dist/core/tools/grep.d.ts +11 -1
  62. package/dist/core/tools/grep.d.ts.map +1 -1
  63. package/dist/core/tools/grep.js +198 -194
  64. package/dist/core/tools/grep.js.map +1 -1
  65. package/dist/core/tools/index.d.ts +31 -29
  66. package/dist/core/tools/index.d.ts.map +1 -1
  67. package/dist/core/tools/index.js +44 -16
  68. package/dist/core/tools/index.js.map +1 -1
  69. package/dist/core/tools/ls.d.ts +6 -1
  70. package/dist/core/tools/ls.d.ts.map +1 -1
  71. package/dist/core/tools/ls.js +90 -86
  72. package/dist/core/tools/ls.js.map +1 -1
  73. package/dist/core/tools/path-utils.d.ts +6 -1
  74. package/dist/core/tools/path-utils.d.ts.map +1 -1
  75. package/dist/core/tools/path-utils.js +17 -5
  76. package/dist/core/tools/path-utils.js.map +1 -1
  77. package/dist/core/tools/read.d.ts +7 -1
  78. package/dist/core/tools/read.d.ts.map +1 -1
  79. package/dist/core/tools/read.js +118 -115
  80. package/dist/core/tools/read.js.map +1 -1
  81. package/dist/core/tools/write.d.ts +6 -1
  82. package/dist/core/tools/write.d.ts.map +1 -1
  83. package/dist/core/tools/write.js +63 -59
  84. package/dist/core/tools/write.js.map +1 -1
  85. package/dist/index.d.ts +2 -1
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +14 -0
  88. package/dist/index.js.map +1 -1
  89. package/dist/main.d.ts +4 -1
  90. package/dist/main.d.ts.map +1 -1
  91. package/dist/main.js +142 -312
  92. package/dist/main.js.map +1 -1
  93. package/dist/modes/interactive/components/session-selector.d.ts +3 -12
  94. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  95. package/dist/modes/interactive/components/session-selector.js +1 -3
  96. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  97. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  98. package/dist/modes/interactive/interactive-mode.js +3 -2
  99. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  100. package/dist/utils/shell.d.ts.map +1 -1
  101. package/dist/utils/shell.js +1 -1
  102. package/dist/utils/shell.js.map +1 -1
  103. package/docs/sdk.md +864 -0
  104. package/examples/README.md +29 -0
  105. package/examples/sdk/01-minimal.ts +22 -0
  106. package/examples/sdk/02-custom-model.ts +36 -0
  107. package/examples/sdk/03-custom-prompt.ts +44 -0
  108. package/examples/sdk/04-skills.ts +44 -0
  109. package/examples/sdk/05-tools.ts +93 -0
  110. package/examples/sdk/06-hooks.ts +61 -0
  111. package/examples/sdk/07-context-files.ts +36 -0
  112. package/examples/sdk/08-slash-commands.ts +37 -0
  113. package/examples/sdk/09-api-keys-and-oauth.ts +45 -0
  114. package/examples/sdk/10-settings.ts +38 -0
  115. package/examples/sdk/11-sessions.ts +46 -0
  116. package/examples/sdk/12-full-control.ts +99 -0
  117. package/examples/sdk/README.md +138 -0
  118. 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
+ ```