Generated banner for Migrating Pi To Pydantic AI
Generated logo for Migrating Pi To Pydantic AI

Migrating Pi To Pydantic AI

PydanticOfficialPublished by Pydantic

  • Skill
  • Workflow
  • Software development
  • MIT
  • No issues found

From the author

Migrate TypeScript Pi coding-agent applications, extensions, or packages to Python with Pydantic AI and Pydantic AI Harness. Use when source code imports `@earendil-works/pi-*`, calls `createAgentSession`, registers Pi extensions, or relies on Pi tools, hooks, skills, sessions, compaction, providers, TUI, RPC, or packages.
— the description by Pydantic

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured methodology for porting TypeScript-based Pi coding agents to Python. It emphasizes preserving observable behavior rather than replicating the original API, guiding developers to decompose complex Pi extensions into modular Pydantic AI capabilities, application-level services, and focused Harness components. The process focuses on tracing existing execution paths, mapping responsibilities to appropriate architectural seams, and verifying parity through rigorous contract testing.

What it does

  • Provides a step-by-step process for tracing and characterizing existing Pi agent behavior.
  • Offers a decision framework for mapping Pi extensions to Pydantic AI capabilities or application logic.
  • Defines strategies for handling complex Pi features like session management, compaction, and tool guardrails.
  • Includes guidance on separating agent-loop logic from host-level infrastructure like CLI, TUI, and RPC.
  • Establishes a verification process using contract ledgers and language-neutral fixtures.
  • Outlines high-risk gates for managing trusted host context and security boundaries.

When to use it

  • When migrating an existing TypeScript Pi coding-agent application to a Python-based Pydantic AI architecture.
  • When refactoring Pi extensions into reusable Pydantic AI capabilities.
  • When you need to maintain parity with existing agent workflows while adopting the Pydantic AI ecosystem.
  • When you need to audit and split mixed-responsibility extensions into distinct agent and application components.

Advantages

  • Ensures architectural alignment with Pydantic AI best practices.
  • Reduces technical debt by decoupling agent behavior from host-specific implementation details.
  • Provides a clear path for verifying migration success through contract-based testing.
  • Promotes a modular design by separating agent capabilities from application infrastructure.

Usage examples

“How do I migrate a Pi extension that handles both tool registration and terminal UI rendering?”
The skill instructs you to split the extension: move the tool registration logic to a Pydantic AI capability and retain the terminal UI rendering within your application's interface layer.
“What is the recommended approach for handling existing Pi session JSONL files during migration?”
The skill advises evaluating whether to perform a data conversion, implement a compatibility reader, or treat the migration as a fresh start, while testing the chosen path against your specific requirements.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/pydantic/migrating-pi-to-pydantic-ai/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip migrating-pi-to-pydantic-ai.zip -d .agents/skills

Documentation by Pydantic

Migrate Pi to Pydantic AI

Preserve observable behavior, not Pi's TypeScript API. Migrate the smallest complete caller path and keep product/host infrastructure in the application.

Trace the source before choosing a target

  1. Read repository instructions, package.json, Pi settings, tests, extension/package manifests, and runtime entrypoints. Record the installed Pi, Pydantic AI, and Harness versions.

  2. Trace one real request from the Pi CLI, RPC boundary, or createAgentSession().prompt() through resource discovery, system instructions, tools, extensions, model requests, messages, compaction/retry, events, session entries, UI/RPC output, and side effects. Establish a focused baseline or characterization test.

  3. Inventory every active extension factory and package resource. For each pi.on(...), registerTool, registerCommand, provider registration, renderer/UI contribution, skill, prompt, and persisted entry, record its firing point, trusted inputs, mutations, output, state owner, and caller-visible effect.

  4. Separate these contracts when present:

    • model messages, append-only session entries/tree branches, compaction summaries, and extension state;

    • model-chosen tool input, trusted host context, project trust, approval, authorization, and OS isolation;

    • token deltas, tool lifecycle, Pi extension events, capability events, RPC events, and terminal rendering;

    • agent-loop behavior, CLI/TUI host behavior, provider transport, package distribution, and external side effects.

  5. Record each observed contract, its owner, semantic difference, and executable proof. An installed but inactive Pi package feature is not migration scope.

Read Research and concept mapping for the detected features. Read Verification and cutover before implementation.

Treat extensions as capability candidates

A Pi extension is the closest source concept to a Pydantic AI capability, but it is broader. An extension can combine agent behavior with terminal UI, commands, provider registration, resource discovery, and host lifecycle. Port responsibilities, not the extension file:

  1. Map reusable model-facing behavior—tools, instructions, model settings, history/event processing, guardrails, and agent-loop hooks—to an existing Core or Harness capability when its lifecycle matches.

  2. Bundle related instructions and tools in core Capability; subclass AbstractCapability only for reusable behavior that needs lifecycle hooks, adaptive models/settings, native tools, or typed capability events.

  3. Keep CLI commands, flags, keyboard shortcuts, TUI renderers/dialogs, session selection, project trust, package installation, and provider credential setup in the Python application or interface adapter. They are not agent capabilities merely because a Pi extension owns them.

  4. Split mixed extensions at that seam. A permission extension may become a ToolGuardrail or approval capability plus an application-owned approver UI; a coding package may become Coder plus host configuration; a provider extension remains a model/provider integration.

  5. Preserve order only where evidence shows it matters. Pi handler load order and Pydantic AI capability/hook ordering are different contracts.

Choose the smallest target

  • Core: use pydantic_ai.Agent for the agent loop, typed dependencies, tools/toolsets, outputs, normalized messages, capabilities/hooks, streaming, approvals, usage limits, instrumentation, MCP, and provider/model integration.

  • Harness: use Coder for Pi's ordinary coding tools only when its exact composition fits. Add focused capabilities such as FileSystem, Shell, Skills, Planning, SubAgents, guardrails, compaction, tool-output limits, or step persistence only for observed behavior.

  • Application/interface: retain or replace CLI/TUI/RPC, auth, project trust, settings, provider login/catalogs, session browsing, package management, deployment, and transport deliberately.

  • Graph: use plain async Python or pydantic_graph for deterministic workflows; do not encode them in prompts or subagents.

  • Gap: name behavior with no supported public seam, explain its impact, and test a bounded adapter. Do not clone Pi's extension bus or JSONL format just to claim parity.

The normal embedded migration is one reusable Agent, Coder or a smaller capability composition, application services in typed dependencies, an application-owned message/session store, and a thin adapter at the existing RPC or UI boundary.

Apply high-risk gates

  • ctx, project trust, credentials, session managers, and service handles are trusted host context. Put equivalents in typed dependencies or application services, never model-chosen tool arguments.

  • Pi session JSONL is an append-only branching host log containing more than model context. result.all_messages() preserves model history, not tree navigation, labels, extension entries, compaction records, model changes, queues, or abandoned branches. Choose and test conversion, a compatibility store, or an accepted fresh start.

  • Pi compaction, context interception, steering/follow-up queues, retries, and branch summaries have specific timing. Select matching core/Harness seams independently and test ordering; generic message history or summarization is not automatic parity.

  • Map tool_call permission gates by effect. Use guardrails for validation/block/redaction and deferred tools for an action that must await approval. Keep identity, authorization, UI, audit, persistence, and idempotency in the application.

  • Pi extensions run arbitrary TypeScript with host permissions. A Pydantic AI capability also runs application code; neither is a sandbox. Use an OS/container/cloud isolation boundary for untrusted commands or code.

  • Harness Skills loads configured SKILL.md instructions on demand but does not reproduce Pi's discovery roots, resource files, scripts, reload, package installation, or behavioral frontmatter. Preserve those separately when observed.

  • Use SubAgents only for model-directed isolated tasks. Keep deterministic orchestration and Pi subprocess/tmux/package-specific semantics in application code.

  • Map dynamic tool activation to core ToolSearch or on-demand capabilities only after testing load timing, schemas, prompt/cache changes, and provider fallback behavior.

  • Pydantic AI run events and capability events do not reproduce Pi's extension event bus, RPC protocol, or TUI render lifecycle. Adapt only stable fields consumers use.

  • Pi provider extensions and payload hooks may alter authentication, catalogs, wire payloads, headers, and streaming. Implement them at the Pydantic AI model/provider or application transport layer and run provider contract tests; do not hide them in a generic capability.

Implement and prove one vertical slice

  1. Preserve the supported CLI, RPC, SDK, job, or UI boundary and replace only agent-owned internals.

  2. Start with core plus the smallest coding capabilities. Add broader Harness behavior only after a traced contract requires it.

  3. For each source extension, document the split: capability behavior, application/interface behavior, retained integration, and gap. Test each side through its real boundary.

  4. Use language-neutral fixtures for RPC/events and persisted records. Test tool arguments/results, errors, event order, session continuation, UI decisions, and side effects. Use deterministic models offline; add focused recorded/live provider tests only for provider behavior.

  5. Remove Pi packages, settings, Node runtime, or extension adapters only after no retained path needs them.

Explain consequential semantic changes before implementing them: state the Pi behavior, target behavior, caller impact, recommended choice, and remaining risk.

Completion

Apply the completion criterion in Verification and cutover. Label fake, recording, live-provider, terminal, restart, and sandbox evidence accurately.

Files

references/VERIFICATION-AND-CUTOVER.md

Verification and cutover

Prove parity through the application's supported boundary. Do not require terminal, provider, persistence, or sandbox exercises for behavior the source slice does not use.

Build a contract ledger

Keep one row for each traced behavior and each responsibility in an active extension:

Source lifecycleObservable contractOwnerTarget differenceEvidence
What initiates and completes itInputs, outputs, events, errors, ordering, state, or effects callers observeApplication, Core, Harness, Graph, or GapWhat will not remain identicalBaseline and focused target test

Use these evidence states:

StatusMeaning
verified-equivalentSource and target preserve the same observable contract in executable checks.
verified-adapterInternal semantics differ, but an adapter preserves the public contract in executable checks.
intentional-changeThe difference and impact were explained and explicitly accepted.
external-ownerA named application or infrastructure component preserves the contract, with evidence at that boundary.
not-applicableThe observed source path does not provide or consume this behavior.
unverifiedEvidence is incomplete or only a candidate design exists; do not call it equivalent.
blockedA required contract has no acceptable proved construction; do not cut over the slice.

Forward-test the extension split

For each active Pi extension or package, record:

Source responsibilityCapability/Core targetApplication/interface targetGapProof
Tool, hook, command, UI, provider, resource, or state behaviorExact reusable agent behaviorHost behavior retained/rebuiltUnsupported differenceExecutable observations on both sides

Reject a one-row "extension becomes capability" claim when the source also owns commands, UI, providers, persistence, discovery, or host lifecycle. Test capability and application halves independently and then through the supported end-to-end boundary.

Match testing to the slice

Ordinary embedded agent or coding tool
  • Characterize prompt() inputs, final messages, errors, usage, and effects before rewriting TypeScript as Python.

  • Use TestModel or FunctionModel to prove prompts, typed dependency-backed tools, output, retries, and messages offline.

  • Compare Pi coding tools with Coder or the selected smaller composition: schemas, exact edit matching, cwd, ignore/protected paths, truncation, command process lifetime, environment, and cancellation.

  • Add provider recordings/live probes only when provider payloads, native tools, reasoning, or deltas are part of the contract.

Extensions, hooks, and capabilities
  • Record a source golden trace of only events and stable fields consumers use.

  • Test handler/capability order, input/result mutation, validation timing, short-circuit behavior, exceptions, stream visibility, cancellation, and state lifetime.

  • For custom capabilities, test composition with siblings, typed event attribution/order, per-run binding, concurrent runs, and cleanup.

  • For mixed extensions, test commands/UI/provider/application behavior separately from the agent capability, then run one integrated boundary test.

Sessions, compaction, and queued input
  • Decide how existing Pi JSONL crosses cutover: conversion, compatibility reader, retained Pi session service, or fresh start as an intentional-change.

  • Test messages separately from tree entries, labels, branches, compactions, extension entries, queued messages, and model/thinking changes.

  • Continue in a fresh process; exercise incomplete tool calls, abandoned branches, overflow retry, repeated compaction, steering/follow-up order, concurrent input, cancellation, and storage failure.

  • Do not claim StepPersistence or serialized model messages preserve Pi session semantics without those checks.

Approvals, trust, and isolation
  • Exercise allow, block, approve, deny, timeout, duplicate decision, and restart. Persist the complete pending action when the decision crosses a process boundary.

  • Assert no protected effect before approval, zero after denial, and exactly one after approval.

  • Test authenticated identity, policy lookup, audit, and correlation in application code.

  • Test project-resource loading before/after trust if retained. State explicitly that trust, path restrictions, and allowlists are not containment.

  • For untrusted commands/code, run escape and credential tests inside the selected container, VM, or cloud sandbox.

Skills, dynamic tools, and subagents
  • Test configured skill roots, catalog descriptions, model-directed loading, missing references/scripts, behavioral frontmatter, trust, and reload/restart.

  • For dynamic tools, assert initial invisibility, selected schema availability on the next request, provider-native/fallback wire behavior, removals, and prompt-cache effects.

  • For subagents, test task-only input, history isolation, shared tools/capabilities, result handback, event propagation, budgets, cancellation, errors, and recursion policy.

RPC, TUI, and provider integrations
  • Capture language-neutral fixtures for each consumed RPC/JSON command and event. Run them against Pi and the target adapter, including errors, abort, mid-run input, and session operations.

  • Use terminal snapshot or interaction tests only for TUI behavior in scope; a passing agent test does not verify dialogs, keyboard handling, custom rendering, or mode fallbacks.

  • For provider extensions, test auth precedence, catalog refresh, model selection, headers/payloads, streaming block assembly, tool calls/results, usage/cost, abort, overflow, and retry against recordings or a controlled server.

Dependency and cutover checks

  1. Resolve the Python project from a clean environment against supported Pydantic AI and Harness versions. Install only the provider and capability extras the slice requires.

  2. Run Pi source characterization tests and target tests through the same supported boundary. Record whether each uses deterministic fakes, wire recordings, live providers, terminal automation, fresh-process restart, or a real sandbox.

  3. Inspect the target wheel when Pi package behavior becomes a Python distribution. Confirm all capability modules and promised skill/assets are present and importable without development dependencies.

  4. Search imports, extension settings, package manifests, resource roots, session readers, provider configuration, RPC/TUI launchers, and deployment scripts for retained @earendil-works/pi-* and Node requirements.

  5. Keep adapters and rollback while callers or old records still need Pi. Remove Pi packages, settings, Node runtime, and JSONL assumptions only after no retained path uses them.

Completion criterion

Cut over only when every ledger row is verified-equivalent, verified-adapter, an accepted intentional-change, external-owner, or not-applicable; no required row is unverified or blocked; every active extension has an evidenced responsibility split; the supported boundary passes; dependencies resolve cleanly; and rollback remains possible.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
Pydantic
Publisher
Pydantic (Official)
Source
pydantic/skills · skills/migrating-pi-to-pydantic-ai · commit 98f591c
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced
Also distributed in
pydantic/skills

AutoGineer is not affiliated with or endorsed by Pydantic or Pydantic.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download migrating-pi-to-pydantic-ai.zip