. pi-agent-core (Runtime Engine): Manages tool calling, state persistence, and session branching. Exposes hooks for custom tool registration.
3. pi-coding-agent (CLI Interface): The interactive terminal wrapper that binds the runtime to user input, file system access, and shell execution.
4. pi-tui (Terminal UI): Implements differential rendering to minimize terminal flicker and maintain responsive output during long-running tool chains.
Step 1: Environment Initialization & Auth Routing
Authentication should be abstracted from your application logic. Rather than scattering API keys across scripts, use a centralized credential resolver that supports both subscription tokens and raw API keys.
import { CredentialResolver, ProviderGateway } from '@earendil-works/pi-ai';
// Factory pattern for auth resolution
const authFactory = async () => {
const resolver = new CredentialResolver({
storagePath: process.env.PI_AUTH_DIR || `${process.env.HOME}/.pi/agent`,
fallbackStrategy: 'env-var',
});
// Supports subscription login or direct key injection
const credentials = await resolver.resolve({
providers: ['anthropic', 'openai', 'google'],
subscriptionMode: process.env.USE_SUBSCRIPTION === 'true',
});
return new ProviderGateway(credentials);
};
Why this choice: Decoupling auth from the runtime prevents credential desynchronization when switching between interactive sessions and CI pipelines. The factory pattern allows you to inject mock credentials for testing without modifying core logic.
Step 2: Context Management & Tool Registration
Context files (AGENTS.md or CLAUDE.md) provide project-specific instructions. Combine this with explicit tool registration to control exactly what the agent can execute.
import { ContextLoader, ToolRegistry, FileScanner } from '@earendil-works/pi-agent-core';
const initializeWorkspace = async (projectRoot: string) => {
// Load global and project-level context with explicit precedence
const contextLoader = new ContextLoader({
globalPath: `${process.env.HOME}/.pi/agent/AGENTS.md`,
projectPath: `${projectRoot}/AGENTS.md`,
mergeStrategy: 'project-overrides',
});
const workspaceContext = await contextLoader.load();
// Register tools with explicit permission boundaries
const toolRegistry = new ToolRegistry();
toolRegistry.register('file-reader', new FileScanner({ readOnly: true }));
toolRegistry.register('patcher', new FilePatcher({ backupEnabled: true }));
toolRegistry.register('shell-exec', new ShellRunner({
timeoutMs: 15000,
allowNetwork: false
}));
return { workspaceContext, toolRegistry };
};
Why this choice: Explicit tool registration prevents accidental execution of destructive commands. The project-overrides merge strategy ensures team-wide policies (global) can be refined per repository (project) without duplication. Backup-enabled patching mitigates data loss during iterative edits.
Step 3: Session Orchestration & SDK Integration
For programmatic usage, the runtime exposes a clean SDK that supports both in-memory and persistent session stores. RPC mode enables polyglot integration via JSONL framing.
import { SessionOrchestrator, ModelRouter, JsonRpcBridge } from '@earendil-works/pi-coding-agent';
const bootstrapAgent = async (gateway: ProviderGateway, registry: ToolRegistry) => {
const router = new ModelRouter({
default: 'anthropic/claude-sonnet-4-20250514',
fallback: 'openai/gpt-4o-mini',
routingRules: {
'code-review': 'anthropic/claude-opus-4-20250514',
'lint-fix': 'openai/gpt-4o-mini',
'architecture': 'google/gemini-2.5-pro',
},
});
const orchestrator = new SessionOrchestrator({
gateway,
registry,
router,
persistence: 'disk', // or 'memory' for ephemeral runs
sessionDir: `${process.env.HOME}/.pi/sessions`,
});
// Start interactive or pipe input via RPC
const session = await orchestrator.createSession({
forkOnBranch: true,
autoSaveInterval: 30000,
});
return session;
};
Why this choice: Model routing based on task type optimizes cost and latency. Disk persistence ensures sessions survive terminal crashes or CI runner recycling. The forkOnBranch flag automatically preserves alternative solution paths, which is critical for complex refactors where linear prompting often loses context.
Pitfall Guide
1. Context File Collision
Explanation: Global and project-level AGENTS.md files can override each other unpredictably if precedence rules aren't explicit. This leads to conflicting instructions (e.g., global says "use pnpm", project says "use yarn").
Fix: Implement a strict merge strategy (project-overrides or global-fallback). Use /reload after modifying context files, and validate loaded instructions with a dry-run prompt before executing critical tasks.
2. Context Window Bloat from Shell Output
Explanation: Using the ! prefix to run commands injects full stdout/stderr into the model's context. Long logs or verbose test runs quickly consume token budgets, degrading reasoning quality.
Fix: Use !! for silent execution when output isn't needed. For necessary logs, pipe to a temporary file and reference it with @. Implement token budgeting in your tool wrapper to truncate output automatically.
3. Ignoring Session Branching
Explanation: Developers often proceed linearly, overwriting previous attempts. When a refactoring path fails, recovering the original state requires manual reconstruction.
Fix: Use /fork or /clone before major structural changes. Treat sessions like Git branches: explore alternatives in parallel, merge successful paths, and discard dead ends without context loss.
4. Auth State Desynchronization
Explanation: Mixing environment variable keys with interactive /login subscription auth creates conflicting credential states. The runtime may attempt to use an expired subscription token while an env var is present.
Fix: Standardize on one authentication method per environment. In CI, use explicit API keys. In local development, prefer subscription login. Validate auth state at startup with a lightweight model ping.
5. RPC Framing Misunderstanding
Explanation: Assuming standard JSON instead of JSONL (JSON Lines) when using --mode rpc causes parsing failures. The runtime streams events line-by-line; buffering until EOF breaks real-time feedback.
Fix: Implement line-buffered parsing on the consumer side. Each line must be a valid JSON object. Use streaming libraries that handle newline delimiters natively, and implement retry logic for partial frames.
6. Over-Extending the Terminal UI
Explanation: Adding heavy UI components or synchronous blocking operations to pi-tui extensions causes terminal flicker and unresponsive rendering. The differential renderer expects lightweight, async-safe updates.
Fix: Keep extensions stateless and event-driven. Use pi-tui primitives for rendering. Offload heavy computation to background workers and emit progress events rather than blocking the main thread.
7. Model Mismatch for Task Complexity
Explanation: Routing all tasks to a single high-capability model wastes tokens and increases latency. Conversely, using lightweight models for architectural planning produces hallucinated implementations.
Fix: Implement task-aware routing. Use fast, cheap models for file scanning, linting, and boilerplate. Reserve reasoning-heavy models for cross-module refactors, security audits, and dependency resolution. Validate routing rules with A/B testing on known codebases.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Solo Developer / Local Workflow | Interactive CLI with subscription auth | Zero per-token cost, full session branching, immediate feedback | $0 (subscription reuse) |
| CI/CD Pipeline Integration | Non-interactive -p mode with --mode json | Deterministic execution, scriptable output, no TUI overhead | Low (API keys or shared subscription) |
| Multi-Provider Enterprise | SDK integration with ModelRouter | Centralized policy enforcement, task-aware routing, audit trails | Medium (managed key rotation, usage monitoring) |
| Polyglot Microservices | RPC mode over stdin/stdout | Language-agnostic integration, streaming JSONL, low footprint | Low (process overhead only) |
Configuration Template
{
"runtime": {
"auth": {
"method": "subscription",
"storage": "~/.pi/agent/auth.json",
"fallback": "env-var"
},
"context": {
"global": "~/.pi/agent/AGENTS.md",
"project": "./AGENTS.md",
"mergeStrategy": "project-overrides",
"autoReload": true
},
"session": {
"persistence": "disk",
"directory": "~/.pi/sessions",
"autoSaveIntervalMs": 30000,
"forkOnBranch": true
},
"tools": {
"shell": {
"timeoutMs": 15000,
"allowNetwork": false,
"outputLimit": 4096
},
"filePatcher": {
"backupEnabled": true,
"dryRunDefault": false
}
},
"modelRouting": {
"default": "anthropic/claude-sonnet-4-20250514",
"fallback": "openai/gpt-4o-mini",
"rules": {
"code-review": "anthropic/claude-opus-4-20250514",
"lint-fix": "openai/gpt-4o-mini",
"architecture": "google/gemini-2.5-pro"
}
}
}
}
Quick Start Guide
- Install the runtime: Run
npm install -g @earendil-works/pi-coding-agent (or your preferred package manager). No Docker or Python dependencies required.
- Authenticate: Execute
pi in any directory and run /login to link an existing subscription, or export ANTHROPIC_API_KEY/OPENAI_API_KEY for direct access.
- Initialize context: Create an
AGENTS.md file in your project root with team-specific rules, lint commands, and architectural constraints.
- Launch a session: Run
pi in your project directory. Use @ to reference files, ! for shell commands, and /fork to branch exploration paths.
- Automate: Pipe input via
cat file.ts | pi -p "Refactor to functional style" or integrate RPC mode into your CI pipeline with --mode json for structured event streaming.