@dex-ai/pi-compat
v0.1.2
Published
Pi extension compatibility layer — load and run pi-coding-agent extensions inside Dex
Readme
@dex-ai/pi-compat
Pi extension compatibility layer — load and run pi-coding-agent extensions inside Dex.
What This Does
Pi has a mature extension ecosystem with powerful tools like:
- pi-lens — LSP + AST-grep code intelligence
- pi-subagents — Multi-agent delegation
- pi-mcp-adapter — MCP server integration
- pi-intercom — Inter-session communication
- @samfp/pi-memory — Persistent memory with consolidation
This package lets you use those extensions in Dex without rewriting them. It creates a shim ExtensionAPI that captures Pi's imperative registrations (tools, event handlers, skills) and maps them to Dex's declarative Extension interface.
Usage
import { piCompatExtension } from '@dex-ai/pi-compat';
import { Agent } from '@dex-ai/sdk';
const agent = await Agent.create({
extensions: [
// Load pi-lens (globally installed)
await piCompatExtension({ packages: ['pi-lens'] }),
// Load multiple Pi extensions at once
await piCompatExtension({
packages: ['pi-lens', '@samfp/pi-memory'],
flags: { 'lens-lsp': true },
}),
// Load from a path
await piCompatExtension({
paths: ['~/.pi/agent/extensions/my-extension'],
}),
],
});How It Works
┌─────────────────────┐ ┌─────────────────────┐ ┌──────────────────┐
│ Pi Extension │ │ pi-compat shim │ │ Dex Extension │
│ │ │ │ │ │
│ pi.registerTool() │────▶│ captures → converts │────▶│ ext.tools[] │
│ pi.on("event") │────▶│ maps event names │────▶│ ext.on.{event} │
│ pi.exec() │────▶│ child_process.spawn │ │ (functional) │
│ pi.registerFlag() │────▶│ flag → env/config │────▶│ ext.config │
│ skills/ directory │────▶│ loads SKILL.md files │────▶│ ext.skills[] │
│ │ │ │ │ │
│ pi.registerShortcut │────▶│ (no-op) │ │ │
│ ctx.ui.* │────▶│ (no-op stubs) │ │ │
│ pi.registerProvider │────▶│ (no-op) │ │ │
└─────────────────────┘ └─────────────────────┘ └──────────────────┘Architecture Translation
| Pi Concept | Dex Equivalent | Status |
|---|---|---|
| pi.registerTool({ name, parameters: Type.Object(...), execute }) | ext.tools: [{ name, parameters: z.object(...), execute }] | ✅ Mapped (TypeBox → Zod) |
| pi.on("session_start", handler) | ext.on: { 'session-start': handler } | ✅ Mapped |
| pi.on("tool_call", handler) | ext.on: { 'tool-start': handler } | ✅ Mapped |
| pi.on("tool_result", handler) | ext.on: { 'tool-stop': handler } | ✅ Mapped |
| pi.on("agent_end", handler) | ext.on: { 'generate-stop': handler } | ✅ Mapped |
| pi.on("turn_start/end") | ext.on: { 'model-start/stop' } | ✅ Mapped |
| pi.exec(cmd, args) | child_process.spawn() | ✅ Functional |
| pi.registerFlag(name, opts) | opts.flags on piCompatExtension | ✅ Via options |
| Skills (package.json "pi".skills) | ext.skills[] (loaded from SKILL.md) | ✅ Loaded |
| pi.registerCommand() | (captured, not exposed) | ⚠️ Captured only |
| pi.registerShortcut() | — | ❌ No-op |
| pi.registerProvider() | — | ❌ No-op |
| pi.sendMessage() | — | ❌ No-op |
| ctx.ui.* | — | ❌ No-op stubs |
Event Mapping
| Pi Event | Dex Event | Notes |
|---|---|---|
| session_start | session-start | Fires on agent init |
| session_shutdown | session-stop | Fires on agent dispose |
| before_agent_start | generate-start | Before each generate() |
| agent_end | generate-stop | After each generate() |
| turn_start | model-start | Before each LLM call |
| turn_end | model-stop | After each LLM call |
| tool_call | tool-start | Before tool execution (can block) |
| tool_result | tool-stop | After tool execution (can modify) |
| context | generate-input | Context injection |
Prerequisites
Pi extensions must be installed globally (or locally) with their dependencies:
# Install pi extensions
npm install -g pi-lens @samfp/pi-memory pi-subagents
# The pi-coding-agent core package must also be installed
# (pi extensions peer-depend on it for types)
npm install -g @earendil-works/pi-coding-agentConfiguration via Flags
Pi extensions use registerFlag() / getFlag() for configuration. Pass flag values via the flags option:
await piCompatExtension({
packages: ['pi-lens'],
flags: {
'lens-lsp': true, // Enable LSP features
'lens-guard': true, // Enable git guard
'lens-verbose': false, // Disable verbose logging
},
});Limitations
No UI — Pi's rich TUI features (overlays, widgets, custom footer) are no-ops. Extensions that are purely UI-focused (pi-powerline-footer) won't provide value.
Event fidelity — Some Pi events have no direct Dex equivalent. Extensions relying heavily on
message_start/update/end,before_provider_request, orinputevents will have those handlers silently ignored.TypeBox → Zod — Schema conversion is best-effort. Complex TypeBox features (discriminated unions with
$ref, conditional schemas) may fall back toz.any().No message injection — Pi's
sendMessage()/sendUserMessage()for steering mid-generation isn't mapped. Extensions that inject messages won't function for that capability.Single provider — Pi's
registerProvider()is a no-op. Extensions that add custom providers won't contribute models.
Development
bun install
bun run typecheck
bun testLicense
MIT
