Development Workflow for Pi-Web: A Complete Guide to Extending the Pi Coding Agent

The pi-web development workflow centers on a Next.js application structure where you modify API routes in app/api/, extend session logic in lib/rpc-manager.ts, and iterate on React components in components/, using npm run dev to hot-reload changes while the global session registry preserves active agent states across refreshes.

Pi-web is the official web interface for the pi coding agent, built as a Next.js application in the agegr/pi-web repository. Understanding the development workflow for pi-web requires navigating three distinct layers: the HTTP API surface, the core runtime wrapping the pi SDK, and the React frontend. This guide walks through each layer with specific file paths and implementation patterns extracted from the source code.

Starting the Development Server

The entry point for local development is the Next.js dev server configured in package.json. Running npm run dev binds the application to 127.0.0.1:30141 by default, enabling hot-reload for all TypeScript and React files.

Unlike production builds, you should avoid running next build during active development to prevent cluttering the .next/ directory with stale artifacts. For production releases, use npm run release, which handles versioning, building, and publishing in one step.

The Three-Layer Architecture

The codebase is organized into three functional layers that handle the flow from HTTP request to UI rendering.

Next.js API Routes (app/api/)

All RPC commands and Server-Sent Events (SSE) stream through /app/api/agent/[id]/route.ts. This file handles POST requests for commands and maintains the event stream connection used by the frontend to receive real-time updates from the agent.

Core Runtime (lib/)

The lib/ directory contains the business logic that bridges the Next.js server and the pi SDK. The AgentSessionWrapper class in lib/rpc-manager.ts manages the lifecycle of AgentSession instances, implementing commands like "prompt", "fork", "navigate_tree", and "set_model".

It maintains a global registry at globalThis.__piSessions that survives Next.js hot-reloads, ensuring persistent session state across code changes. The file also exports hasBusyRpcSessionForCwd() and subscribeRunningSessions() to let the UI query live session status.

React Components (components/)

The UI layer consists of components like SessionSidebar.tsx and ChatWindow.tsx that render the file explorer and chat interface. The useAgentSession.ts hook orchestrates state management, connecting React components to the SSE streams and RPC endpoints defined in the API layer.

Extending RPC Commands and Session Logic

Adding functionality typically involves modifying the RPC handler switch statement or adjusting how session context is built.

Adding a New RPC Command

To implement a new command, modify the send() method's switch block in lib/rpc-manager.ts. The following example adds a "reset_context" command that clears chat history while preserving the session file:

// In lib/rpc-manager.ts, inside the send() switch:
case "reset_context": {
  // Clears the current chat history but keeps the session file.
  this.inner.sessionManager.clearHistory();
  invalidateSessionListCache();
  return { success: true };
}

Managing Global Session State and Caching

When mutating commands complete, call invalidateSessionListCache() or invalidateModelsCache() to ensure the UI reflects current state. These invalidators are defined in lib/session-reader.ts alongside listAllSessions(), which scans the pi session directory and resolves project roots via resolveProject.

The buildSessionContext() function in the same file converts raw .jsonl entries into UI-ready AgentMessage objects, handling tool-call normalization and deferred thinking blocks.

Building Custom Extension UI

Pi-web supports custom UI widgets from extensions through the AgentSessionWrapper, which creates a PlainTextTheme for rendering and supplies a createExtensionUiContext() offering select, confirm, input, editor, and notify methods.

Creating Extension Widgets

Extensions receive a UI context that can register widgets streamed back as SSE events:

// In an extension's entry point (executed by Pi-Web)
export async function activate(context: ExtensionContext) {
  const ui = context.ui; // Provided by Pi-Web
  ui.setWidget(
    "my-status",
    async (tui, theme) => ({
      render: (width) => [`${theme.bold("Status:")} Ready`.padEnd(width)],
    }),
    { placement: "aboveEditor" },
  );
}

Accessing Session Files from Components

React components can access the underlying session manager to retrieve file paths or metadata. Import getRpcSession from the RPC manager to fetch the wrapper instance:

import { getRpcSession } from "../lib/rpc-manager";

async function openSessionFile(sessionId: string) {
  const wrapper = getRpcSession(sessionId);
  if (!wrapper) return;
  const path = await wrapper.inner.sessionManager.getSessionFile();
  // Use the path to display the raw JSONL or export HTML.
}

Testing, Linting, and Quality Assurance

Before committing changes, run the quality checks defined in package.json. Execute npm test for the test suite, npm run lint for style compliance, and node_modules/.bin/tsc --noEmit for type checking without emission.

For production releases, use npm run release, which handles version bumping, building, and publishing—avoid manual next build calls that complicate the workspace with unnecessary artifacts.

Summary

  • Start local development with npm run dev on port 30141; avoid next build during active coding.
  • API routes in app/api/agent/[id]/route.ts handle RPC and SSE streaming for real-time UI updates.
  • lib/rpc-manager.ts implements the AgentSessionWrapper with a global registry (globalThis.__piSessions) that persists across hot-reloads.
  • lib/session-reader.ts manages session caching and context building via listAllSessions() and buildSessionContext().
  • Invalidate caches using invalidateSessionListCache() after mutations to keep the sidebar and model selectors synchronized.
  • Extension UI integrates through the wrapper's theme system and headless TUI (createHeadlessCustomUiTui).
  • Run npm run release for production builds instead of manual build commands.

Frequently Asked Questions

How do I add a new RPC command to pi-web?

Add a new case to the send() method's switch block in lib/rpc-manager.ts. Implement the logic using the this.inner.sessionManager API, then return a success response. Remember to call invalidateSessionListCache() if the command modifies session metadata that appears in the sidebar.

What is the AgentSessionWrapper and why does it use a global registry?

AgentSessionWrapper is a class that encapsulates the pi SDK's AgentSession and implements the RPC contract. It stores instances in globalThis.__piSessions to survive Next.js hot-reloads, preventing session loss during development while enabling the UI to query running sessions via hasBusyRpcSessionForCwd().

How does hot-reloading affect active agent sessions?

Next.js hot-reload refreshes the API route modules and React components, but the global registry in globalThis persists. This means active AgentSessionWrapper instances continue running, though you may need to invalidate caches to see metadata changes in SessionSidebar.tsx.

How do I implement custom extension UI components?

Extensions access the UI context through the AgentSessionWrapper, which provides createExtensionUiContext(). Use methods like ui.setWidget() to register components that render via the PlainTextTheme and stream updates through the headless TUI system back to the React frontend via SSE.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →