Pi School

Lesson 12

Extensions

Extend Pi's own behavior with TypeScript extensions.

In the Tools lesson, you connected an MCP server to give Pi access to an external tool. That’s one way to extend Pi — but it only adds tools. What if you want to hook into Pi’s own lifecycle, add a custom slash command, or change how Pi behaves rather than just what it can reach?

That’s what extensions are for. An extension is a TypeScript module Pi loads at startup. It can subscribe to events, register custom tools and commands, and even build its own terminal UI — this is Pi’s flagship way of customizing itself.

Where extensions live

  • Global~/.pi/agent/extensions/, as either a *.ts file or a */index.ts subdirectory. Available in every project.
  • Project.pi/extensions/, same file layout. Only loaded once the project is trusted.

Extensions in these locations are hot-reloaded with /reload — no restart needed for changes to existing extensions, though newly installed extensions still need Pi to discover them at startup.

What extensions can do

A minimal extension is just a function that receives an API object:

export default function (pi) {
  pi.on("session_start", async (event, ctx) => {
    // React to a session starting
  });

  pi.registerCommand("hello", {
    description: "Say hello",
    async execute(args, ctx) {
      ctx.ui.notify("Hello!");
    },
  });
}

A few of the things extensions can do:

  • pi.on(event, handler) — subscribe to lifecycle events like a session starting, a tool being called, or the model changing.
  • pi.registerTool(definition) — register a custom tool the LLM can call, the same way built-in tools like read or bash work.
  • pi.registerCommand(name, options) — add a new /command that shows up alongside Pi’s built-in ones.
  • ctx.ui.custom() — build a full custom TUI component with its own keyboard handling, for interactions more complex than plain text.

Extensions can also hold state across a session — well-behaved ones store it in a tool result’s details field so it reconstructs correctly if you branch the conversation (covered in the Branching lesson).

Pi ships more than 60 example extensions covering patterns like these — dynamic tool registration, custom footers, session handoff, and more. Browse them for inspiration before writing your own.

Install a real extension: pi-web-access

Pi has no built-in web search or URL-fetching tool. pi-web-access fills that gap — it adds web_search and fetch_content tools that handle regular web pages, GitHub repos (cloned locally instead of scraped), PDFs, and YouTube videos.

Install it:

pi install npm:pi-web-access

It works immediately with no API keys required. Run /reload to pick it up, then try it:

Search the web for what’s new in TypeScript 5.9

A note on modes

If you’ve used agent tools with a built-in “Plan mode” that separates planning from execution, you might look for the same thing in Pi’s settings — it isn’t there, since Pi has no built-in modes. The bundled plan-mode.ts example extension shows how to build that behavior yourself if you want it.

Find more extensions and write your own

Ask Pi to build you an extension for a workflow you want to automate — describe what you want and it can scaffold one using the patterns above. For the full API reference and all bundled examples, see Pi’s own extensions documentation.