The --no-skills flag set options.skills = [] in main.ts, but the
interactive mode UI would rediscover skills anyway because it called
loadSkills() directly instead of using the already-loaded skills.
Changes:
- Add AgentSession.skills and AgentSession.skillWarnings properties
- discoverSkills() now returns { skills, warnings } instead of Skill[]
- Interactive mode uses session.skills instead of calling loadSkills()
- Update SDK docs and examples for new return type
Fixes #577
29 KiB
pi can help you use the SDK. Ask it to build an integration for your use case.
SDK
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.
Example use cases:
- Build a custom UI (web, desktop, mobile)
- Integrate agent capabilities into existing applications
- Create automated pipelines with agent reasoning
- Build custom tools that spawn sub-agents
- Test agent behavior programmatically
See examples/sdk/ for working examples from minimal to full control.
Quick Start
import { createAgentSession, discoverAuthStorage, discoverModels, SessionManager } from "@mariozechner/pi-coding-agent";
// Set up credential storage and model registry
const authStorage = discoverAuthStorage();
const modelRegistry = discoverModels(authStorage);
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
authStorage,
modelRegistry,
});
session.subscribe((event) => {
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
process.stdout.write(event.assistantMessageEvent.delta);
}
});
await session.prompt("What files are in the current directory?");
Installation
npm install @mariozechner/pi-coding-agent
The SDK is included in the main package. No separate installation needed.
Core Concepts
createAgentSession()
The main factory function. Creates an AgentSession with configurable options.
Philosophy: "Omit to discover, provide to override."
- Omit an option → pi discovers/loads from standard locations
- Provide an option → your value is used, discovery skipped for that option
import { createAgentSession } from "@mariozechner/pi-coding-agent";
// Minimal: all defaults (discovers everything from cwd and ~/.pi/agent)
const { session } = await createAgentSession();
// Custom: override specific options
const { session } = await createAgentSession({
model: myModel,
systemPrompt: "You are helpful.",
tools: [readTool, bashTool],
sessionManager: SessionManager.inMemory(),
});
AgentSession
The session manages the agent lifecycle, message history, and event streaming.
interface AgentSession {
// Send a prompt and wait for completion
// If streaming, requires streamingBehavior option to queue the message
prompt(text: string, options?: PromptOptions): Promise<void>;
// Queue messages during streaming
steer(text: string): Promise<void>; // Interrupt: delivered after current tool, skips remaining
followUp(text: string): Promise<void>; // Wait: delivered only when agent finishes
// Subscribe to events (returns unsubscribe function)
subscribe(listener: (event: AgentSessionEvent) => void): () => void;
// Session info
sessionFile: string | undefined; // undefined for in-memory
sessionId: string;
// Model control
setModel(model: Model): Promise<void>;
setThinkingLevel(level: ThinkingLevel): void;
cycleModel(): Promise<ModelCycleResult | undefined>;
cycleThinkingLevel(): ThinkingLevel | undefined;
// State access
agent: Agent;
model: Model | undefined;
thinkingLevel: ThinkingLevel;
messages: AgentMessage[];
isStreaming: boolean;
// Session management
newSession(options?: { parentSession?: string }): Promise<boolean>; // Returns false if cancelled by hook
switchSession(sessionPath: string): Promise<boolean>;
// Branching
branch(entryId: string): Promise<{ selectedText: string; cancelled: boolean }>; // Creates new session file
navigateTree(targetId: string, options?: { summarize?: boolean }): Promise<{ editorText?: string; cancelled: boolean }>; // In-place navigation
// Hook message injection
sendHookMessage(message: HookMessage, triggerTurn?: boolean): Promise<void>;
// Compaction
compact(customInstructions?: string): Promise<CompactionResult>;
abortCompaction(): void;
// Abort current operation
abort(): Promise<void>;
// Cleanup
dispose(): void;
}
Prompting and Message Queueing
The prompt() method handles prompt templates, extension commands, and message sending:
// Basic prompt (when not streaming)
await session.prompt("What files are here?");
// With images
await session.prompt("What's in this image?", {
images: [{ type: "image", source: { type: "base64", mediaType: "image/png", data: "..." } }]
});
// During streaming: must specify how to queue the message
await session.prompt("Stop and do this instead", { streamingBehavior: "steer" });
await session.prompt("After you're done, also check X", { streamingBehavior: "followUp" });
Behavior:
- Extension commands (e.g.,
/mycommand): Execute immediately, even during streaming. They manage their own LLM interaction viapi.sendMessage(). - File-based prompt templates (from
.mdfiles): Expanded to their content before sending/queueing. - During streaming without
streamingBehavior: Throws an error. Usesteer()orfollowUp()directly, or specify the option.
For explicit queueing during streaming:
// Interrupt the agent (delivered after current tool, skips remaining tools)
await session.steer("New instruction");
// Wait for agent to finish (delivered only when agent stops)
await session.followUp("After you're done, also do this");
Both steer() and followUp() expand file-based prompt templates but error on extension commands (extension commands cannot be queued).
Agent and AgentState
The Agent class (from @mariozechner/pi-agent-core) handles the core LLM interaction. Access it via session.agent.
// Access current state
const state = session.agent.state;
// state.messages: AgentMessage[] - conversation history
// state.model: Model - current model
// state.thinkingLevel: ThinkingLevel - current thinking level
// state.systemPrompt: string - system prompt
// state.tools: Tool[] - available tools
// Replace messages (useful for branching, restoration)
session.agent.replaceMessages(messages);
// Wait for agent to finish processing
await session.agent.waitForIdle();
Events
Subscribe to events to receive streaming output and lifecycle notifications.
session.subscribe((event) => {
switch (event.type) {
// Streaming text from assistant
case "message_update":
if (event.assistantMessageEvent.type === "text_delta") {
process.stdout.write(event.assistantMessageEvent.delta);
}
if (event.assistantMessageEvent.type === "thinking_delta") {
// Thinking output (if thinking enabled)
}
break;
// Tool execution
case "tool_execution_start":
console.log(`Tool: ${event.toolName}`);
break;
case "tool_execution_update":
// Streaming tool output
break;
case "tool_execution_end":
console.log(`Result: ${event.isError ? "error" : "success"}`);
break;
// Message lifecycle
case "message_start":
// New message starting
break;
case "message_end":
// Message complete
break;
// Agent lifecycle
case "agent_start":
// Agent started processing prompt
break;
case "agent_end":
// Agent finished (event.messages contains new messages)
break;
// Turn lifecycle (one LLM response + tool calls)
case "turn_start":
break;
case "turn_end":
// event.message: assistant response
// event.toolResults: tool results from this turn
break;
// Session events (auto-compaction, retry)
case "auto_compaction_start":
case "auto_compaction_end":
case "auto_retry_start":
case "auto_retry_end":
break;
}
});
Options Reference
Directories
const { session } = await createAgentSession({
// Working directory for project-local discovery
cwd: process.cwd(), // default
// Global config directory
agentDir: "~/.pi/agent", // default (expands ~)
});
cwd is used for:
- Project extensions (
.pi/extensions/) - Project skills (
.pi/skills/) - Project prompts (
.pi/prompts/) - Context files (
AGENTS.mdwalking up from cwd) - Session directory naming
agentDir is used for:
- Global extensions (
extensions/) - Global skills (
skills/) - Global prompts (
prompts/) - Global context file (
AGENTS.md) - Settings (
settings.json) - Custom models (
models.json) - Credentials (
auth.json) - Sessions (
sessions/)
Model
import { getModel } from "@mariozechner/pi-ai";
import { discoverAuthStorage, discoverModels } from "@mariozechner/pi-coding-agent";
const authStorage = discoverAuthStorage();
const modelRegistry = discoverModels(authStorage);
// Find specific built-in model (doesn't check if API key exists)
const opus = getModel("anthropic", "claude-opus-4-5");
if (!opus) throw new Error("Model not found");
// Find any model by provider/id, including custom models from models.json
// (doesn't check if API key exists)
const customModel = modelRegistry.find("my-provider", "my-model");
// Get only models that have valid API keys configured
const available = await modelRegistry.getAvailable();
const { session } = await createAgentSession({
model: opus,
thinkingLevel: "medium", // off, minimal, low, medium, high, xhigh
// Models for cycling (Ctrl+P in interactive mode)
scopedModels: [
{ model: opus, thinkingLevel: "high" },
{ model: haiku, thinkingLevel: "off" },
],
authStorage,
modelRegistry,
});
If no model is provided:
- Tries to restore from session (if continuing)
- Uses default from settings
- Falls back to first available model
API Keys and OAuth
API key resolution priority (handled by AuthStorage):
- Runtime overrides (via
setRuntimeApiKey, not persisted) - Stored credentials in
auth.json(API keys or OAuth tokens) - Environment variables (
ANTHROPIC_API_KEY,OPENAI_API_KEY, etc.) - Fallback resolver (for custom provider keys from
models.json)
import { AuthStorage, ModelRegistry, discoverAuthStorage, discoverModels } from "@mariozechner/pi-coding-agent";
// Default: uses ~/.pi/agent/auth.json and ~/.pi/agent/models.json
const authStorage = discoverAuthStorage();
const modelRegistry = discoverModels(authStorage);
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
authStorage,
modelRegistry,
});
// Runtime API key override (not persisted to disk)
authStorage.setRuntimeApiKey("anthropic", "sk-my-temp-key");
// Custom auth storage location
const customAuth = new AuthStorage("/my/app/auth.json");
const customRegistry = new ModelRegistry(customAuth, "/my/app/models.json");
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
authStorage: customAuth,
modelRegistry: customRegistry,
});
// No custom models.json (built-in models only)
const simpleRegistry = new ModelRegistry(authStorage);
System Prompt
const { session } = await createAgentSession({
// Replace entirely
systemPrompt: "You are a helpful assistant.",
// Or modify default (receives default, returns modified)
systemPrompt: (defaultPrompt) => {
return `${defaultPrompt}\n\n## Additional Rules\n- Be concise`;
},
});
Tools
import {
codingTools, // read, bash, edit, write (default)
readOnlyTools, // read, grep, find, ls
readTool, bashTool, editTool, writeTool,
grepTool, findTool, lsTool,
} from "@mariozechner/pi-coding-agent";
// Use built-in tool set
const { session } = await createAgentSession({
tools: readOnlyTools,
});
// Pick specific tools
const { session } = await createAgentSession({
tools: [readTool, bashTool, grepTool],
});
Tools with Custom cwd
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:
import {
createCodingTools, // Creates [read, bash, edit, write] for specific cwd
createReadOnlyTools, // Creates [read, grep, find, ls] for specific cwd
createReadTool,
createBashTool,
createEditTool,
createWriteTool,
createGrepTool,
createFindTool,
createLsTool,
} from "@mariozechner/pi-coding-agent";
const cwd = "/path/to/project";
// Use factory for tool sets
const { session } = await createAgentSession({
cwd,
tools: createCodingTools(cwd), // Tools resolve paths relative to cwd
});
// Or pick specific tools
const { session } = await createAgentSession({
cwd,
tools: [createReadTool(cwd), createBashTool(cwd), createGrepTool(cwd)],
});
When you don't need factories:
- If you omit
tools, pi automatically creates them with the correctcwd - If you use
process.cwd()as yourcwd, the pre-built instances work fine
When you must use factories:
- When you specify both
cwd(different fromprocess.cwd()) ANDtools
Custom Tools
import { Type } from "@sinclair/typebox";
import { createAgentSession, type ToolDefinition } from "@mariozechner/pi-coding-agent";
// Inline custom tool
const myTool: ToolDefinition = {
name: "my_tool",
label: "My Tool",
description: "Does something useful",
parameters: Type.Object({
input: Type.String({ description: "Input value" }),
}),
execute: async (toolCallId, params, onUpdate, ctx, signal) => ({
content: [{ type: "text", text: `Result: ${params.input}` }],
details: {},
}),
};
// Pass custom tools directly
const { session } = await createAgentSession({
customTools: [myTool],
});
Custom tools passed via customTools are combined with extension-registered tools. Extensions discovered from ~/.pi/agent/extensions/ and .pi/extensions/ can also register tools via pi.registerTool().
Extensions
By default, extensions are discovered from multiple locations:
~/.pi/agent/extensions/(global).pi/extensions/(project-local)- Paths listed in
settings.json"extensions"array
import { createAgentSession, type ExtensionFactory } from "@mariozechner/pi-coding-agent";
// Inline extension factory
const myExtension: ExtensionFactory = (pi) => {
pi.on("tool_call", async (event, ctx) => {
console.log(`Tool: ${event.toolName}`);
});
pi.registerCommand("hello", {
description: "Say hello",
handler: async (args, ctx) => {
ctx.ui.notify("Hello!", "info");
},
});
};
// Pass inline extensions (skips file discovery)
const { session } = await createAgentSession({
extensions: [myExtension],
});
// Add paths to load (merged with discovery)
const { session } = await createAgentSession({
additionalExtensionPaths: ["/path/to/my-extension.ts"],
});
// Disable extension discovery entirely
const { session } = await createAgentSession({
extensions: [],
});
Extensions can register tools, subscribe to events, add commands, and more. See extensions.md for the full API.
Event Bus: Extensions can communicate via pi.events. Pass a shared eventBus to createAgentSession() if you need to emit/listen from outside:
import { createAgentSession, createEventBus } from "@mariozechner/pi-coding-agent";
const eventBus = createEventBus();
const { session } = await createAgentSession({ eventBus });
// Listen for events from extensions
eventBus.on("my-extension:status", (data) => console.log(data));
Skills
import { createAgentSession, discoverSkills, type Skill } from "@mariozechner/pi-coding-agent";
// Discover and filter
const { skills: allSkills, warnings } = discoverSkills();
const filtered = allSkills.filter(s => s.name.includes("search"));
// Custom skill
const mySkill: Skill = {
name: "my-skill",
description: "Custom instructions",
filePath: "/path/to/SKILL.md",
baseDir: "/path/to",
source: "custom",
};
const { session } = await createAgentSession({
skills: [...filtered, mySkill],
});
// Disable skills
const { session } = await createAgentSession({
skills: [],
});
// Discovery with settings filter
const { skills } = discoverSkills(process.cwd(), undefined, {
ignoredSkills: ["browser-*"], // glob patterns to exclude
includeSkills: ["search-*"], // glob patterns to include (empty = all)
});
Context Files
import { createAgentSession, discoverContextFiles } from "@mariozechner/pi-coding-agent";
// Discover AGENTS.md files
const discovered = discoverContextFiles();
// Add custom context
const { session } = await createAgentSession({
contextFiles: [
...discovered,
{
path: "/virtual/AGENTS.md",
content: "# Guidelines\n\n- Be concise\n- Use TypeScript",
},
],
});
// Disable context files
const { session } = await createAgentSession({
contextFiles: [],
});
Slash Commands
import { createAgentSession, discoverPromptTemplates, type PromptTemplate } from "@mariozechner/pi-coding-agent";
const discovered = discoverPromptTemplates();
const customCommand: PromptTemplate = {
name: "deploy",
description: "Deploy the application",
source: "(custom)",
content: "# Deploy\n\n1. Build\n2. Test\n3. Deploy",
};
const { session } = await createAgentSession({
promptTemplates: [...discovered, customCommand],
});
Session Management
Sessions use a tree structure with id/parentId linking, enabling in-place branching.
import { createAgentSession, SessionManager } from "@mariozechner/pi-coding-agent";
// In-memory (no persistence)
const { session } = await createAgentSession({
sessionManager: SessionManager.inMemory(),
});
// New persistent session
const { session } = await createAgentSession({
sessionManager: SessionManager.create(process.cwd()),
});
// Continue most recent
const { session, modelFallbackMessage } = await createAgentSession({
sessionManager: SessionManager.continueRecent(process.cwd()),
});
if (modelFallbackMessage) {
console.log("Note:", modelFallbackMessage);
}
// Open specific file
const { session } = await createAgentSession({
sessionManager: SessionManager.open("/path/to/session.jsonl"),
});
// List available sessions
const sessions = SessionManager.list(process.cwd());
for (const info of sessions) {
console.log(`${info.id}: ${info.firstMessage} (${info.messageCount} messages)`);
}
// Custom session directory (no cwd encoding)
const customDir = "/path/to/my-sessions";
const { session } = await createAgentSession({
sessionManager: SessionManager.create(process.cwd(), customDir),
});
SessionManager tree API:
const sm = SessionManager.open("/path/to/session.jsonl");
// Tree traversal
const entries = sm.getEntries(); // All entries (excludes header)
const tree = sm.getTree(); // Full tree structure
const path = sm.getPath(); // Path from root to current leaf
const leaf = sm.getLeafEntry(); // Current leaf entry
const entry = sm.getEntry(id); // Get entry by ID
const children = sm.getChildren(id); // Direct children of entry
// Labels
const label = sm.getLabel(id); // Get label for entry
sm.appendLabelChange(id, "checkpoint"); // Set label
// Branching
sm.branch(entryId); // Move leaf to earlier entry
sm.branchWithSummary(id, "Summary..."); // Branch with context summary
sm.createBranchedSession(leafId); // Extract path to new file
Settings Management
import { createAgentSession, SettingsManager, SessionManager } from "@mariozechner/pi-coding-agent";
// Default: loads from files (global + project merged)
const { session } = await createAgentSession({
settingsManager: SettingsManager.create(),
});
// With overrides
const settingsManager = SettingsManager.create();
settingsManager.applyOverrides({
compaction: { enabled: false },
retry: { enabled: true, maxRetries: 5 },
});
const { session } = await createAgentSession({ settingsManager });
// In-memory (no file I/O, for testing)
const { session } = await createAgentSession({
settingsManager: SettingsManager.inMemory({ compaction: { enabled: false } }),
sessionManager: SessionManager.inMemory(),
});
// Custom directories
const { session } = await createAgentSession({
settingsManager: SettingsManager.create("/custom/cwd", "/custom/agent"),
});
Static factories:
SettingsManager.create(cwd?, agentDir?)- Load from filesSettingsManager.inMemory(settings?)- No file I/O
Project-specific settings:
Settings load from two locations and merge:
- Global:
~/.pi/agent/settings.json - Project:
<cwd>/.pi/settings.json
Project overrides global. Nested objects merge keys. Setters only modify global (project is read-only for version control).
Discovery Functions
All discovery functions accept optional cwd and agentDir parameters.
import { getModel } from "@mariozechner/pi-ai";
import {
AuthStorage,
ModelRegistry,
discoverAuthStorage,
discoverModels,
discoverSkills,
discoverHooks,
discoverCustomTools,
discoverContextFiles,
discoverPromptTemplates,
loadSettings,
buildSystemPrompt,
} from "@mariozechner/pi-coding-agent";
// Auth and Models
const authStorage = discoverAuthStorage(); // ~/.pi/agent/auth.json
const modelRegistry = discoverModels(authStorage); // + ~/.pi/agent/models.json
const allModels = modelRegistry.getAll(); // All models (built-in + custom)
const available = await modelRegistry.getAvailable(); // Only models with API keys
const model = modelRegistry.find("provider", "id"); // Find specific model
const builtIn = getModel("anthropic", "claude-opus-4-5"); // Built-in only
// Skills
const { skills, warnings } = discoverSkills(cwd, agentDir, skillsSettings);
// Hooks (async - loads TypeScript)
// Pass eventBus to share pi.events across hooks/tools
const eventBus = createEventBus();
const hooks = await discoverHooks(eventBus, cwd, agentDir);
// Custom tools (async - loads TypeScript)
const tools = await discoverCustomTools(eventBus, cwd, agentDir);
// Context files
const contextFiles = discoverContextFiles(cwd, agentDir);
// Prompt templates
const commands = discoverPromptTemplates(cwd, agentDir);
// Settings (global + project merged)
const settings = loadSettings(cwd, agentDir);
// Build system prompt manually
const prompt = buildSystemPrompt({
skills,
contextFiles,
appendPrompt: "Additional instructions",
cwd,
});
Return Value
createAgentSession() returns:
interface CreateAgentSessionResult {
// The session
session: AgentSession;
// Extensions result (for runner setup)
extensionsResult: LoadExtensionsResult;
// Warning if session model couldn't be restored
modelFallbackMessage?: string;
}
interface LoadExtensionsResult {
extensions: Extension[];
errors: Array<{ path: string; error: string }>;
runtime: ExtensionRuntime;
}
Complete Example
import { getModel } from "@mariozechner/pi-ai";
import { Type } from "@sinclair/typebox";
import {
AuthStorage,
createAgentSession,
ModelRegistry,
SessionManager,
SettingsManager,
readTool,
bashTool,
type HookFactory,
type CustomTool,
} from "@mariozechner/pi-coding-agent";
// Set up auth storage (custom location)
const authStorage = new AuthStorage("/custom/agent/auth.json");
// Runtime API key override (not persisted)
if (process.env.MY_KEY) {
authStorage.setRuntimeApiKey("anthropic", process.env.MY_KEY);
}
// Model registry (no custom models.json)
const modelRegistry = new ModelRegistry(authStorage);
// Inline hook
const auditHook: HookFactory = (api) => {
api.on("tool_call", async (event) => {
console.log(`[Audit] ${event.toolName}`);
return undefined;
});
};
// Inline tool
const statusTool: CustomTool = {
name: "status",
label: "Status",
description: "Get system status",
parameters: Type.Object({}),
execute: async () => ({
content: [{ type: "text", text: `Uptime: ${process.uptime()}s` }],
details: {},
}),
};
const model = getModel("anthropic", "claude-opus-4-5");
if (!model) throw new Error("Model not found");
// In-memory settings with overrides
const settingsManager = SettingsManager.inMemory({
compaction: { enabled: false },
retry: { enabled: true, maxRetries: 2 },
});
const { session } = await createAgentSession({
cwd: process.cwd(),
agentDir: "/custom/agent",
model,
thinkingLevel: "off",
authStorage,
modelRegistry,
systemPrompt: "You are a minimal assistant. Be concise.",
tools: [readTool, bashTool],
customTools: [{ tool: statusTool }],
hooks: [{ factory: auditHook }],
skills: [],
contextFiles: [],
promptTemplates: [],
sessionManager: SessionManager.inMemory(),
settingsManager,
});
session.subscribe((event) => {
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
process.stdout.write(event.assistantMessageEvent.delta);
}
});
await session.prompt("Get status and list files.");
Run Modes
The SDK exports run mode utilities for building custom interfaces on top of createAgentSession():
InteractiveMode
Full TUI interactive mode with editor, chat history, and all built-in commands:
import { createAgentSession, InteractiveMode } from "@mariozechner/pi-coding-agent";
const { session } = await createAgentSession({ /* ... */ });
const mode = new InteractiveMode(session, {
// All optional
migratedProviders: [], // Show migration warnings
modelFallbackMessage: undefined, // Show model restore warning
initialMessage: "Hello", // Send on startup
initialImages: [], // Images with initial message
initialMessages: [], // Additional startup prompts
});
await mode.run(); // Blocks until exit
runPrintMode
Single-shot mode: send prompts, output result, exit:
import { createAgentSession, runPrintMode } from "@mariozechner/pi-coding-agent";
const { session } = await createAgentSession({ /* ... */ });
await runPrintMode(session, {
mode: "text", // "text" for final response, "json" for all events
initialMessage: "Hello", // First message (can include @file content)
initialImages: [], // Images with initial message
messages: ["Follow up"], // Additional prompts
});
runRpcMode
JSON-RPC mode for subprocess integration:
import { createAgentSession, runRpcMode } from "@mariozechner/pi-coding-agent";
const { session } = await createAgentSession({ /* ... */ });
await runRpcMode(session); // Reads JSON commands from stdin, writes to stdout
See RPC documentation for the JSON protocol.
RPC Mode Alternative
For subprocess-based integration without building with the SDK, use the CLI directly:
pi --mode rpc --no-session
See RPC documentation for the JSON protocol.
The SDK is preferred when:
- You want type safety
- You're in the same Node.js process
- You need direct access to agent state
- You want to customize tools/hooks programmatically
RPC mode is preferred when:
- You're integrating from another language
- You want process isolation
- You're building a language-agnostic client
Exports
The main entry point exports:
// Factory
createAgentSession
// Auth and Models
AuthStorage
ModelRegistry
discoverAuthStorage
discoverModels
// Discovery
discoverSkills
discoverHooks
discoverCustomTools
discoverContextFiles
discoverPromptTemplates
// Event Bus (for shared hook/tool communication)
createEventBus
// Helpers
loadSettings
buildSystemPrompt
// Session management
SessionManager
SettingsManager
// Built-in tools (use process.cwd())
codingTools
readOnlyTools
readTool, bashTool, editTool, writeTool
grepTool, findTool, lsTool
// Tool factories (for custom cwd)
createCodingTools
createReadOnlyTools
createReadTool, createBashTool, createEditTool, createWriteTool
createGrepTool, createFindTool, createLsTool
// Types
type CreateAgentSessionOptions
type CreateAgentSessionResult
type CustomTool
type HookFactory
type Skill
type PromptTemplate
type Settings
type SkillsSettings
type Tool
For hook types, import from the hooks subpath:
import type {
HookAPI,
HookMessage,
HookFactory,
HookEventContext,
HookCommandContext,
ToolCallEvent,
ToolResultEvent,
} from "@mariozechner/pi-coding-agent/hooks";
For message utilities:
import { isHookMessage, createHookMessage } from "@mariozechner/pi-coding-agent";
For config utilities:
import { getAgentDir } from "@mariozechner/pi-coding-agent/config";