Pi-Bolt Docs

Guides

Extensions

Pi extensions and Pi packages work in Pi-Bolt without changes. They are found, installed and configured exactly as in Pi. What differs is how their code runs, which depends on the build.

Install and manage extensions#

Pi-Bolt loads extensions from the same places as Pi: ~/.pi/agent/extensions, a project's .pi/extensions, Pi packages, and -e <path> on the command line.

$ pi-bolt install npm:opensec-pi-todo     # install a Pi package
$ pi-bolt list                           # list installed packages
$ pi-bolt update --extensions            # update them
$ pi-bolt remove npm:opensec-pi-todo     # remove one
$ pi-bolt config                         # turn resources on and off

pi-bolt install and pi-bolt remove change Pi-Bolt's own package list, not Pi's: what you install with pi-bolt does not load in Pi (see Configuration).

Pi-Bolt installs packages with its own package manager when npm is not installed. A run-time extension's own dependencies resolve as they do on Node, through its package.json. Writing extensions is covered by Pi's Extensions guide.

How extensions run#

Loaded at run time, default buildLoaded at run time, JIT buildCompiled in
Changes neededNoneNoneUsually none (see the checklist)
Startup costTypeScript compiled at each startTypeScript compiled at each startNone
Hot code runs asBytecode interpreterInterpreter, then JITMachine code
Update the extensionEdit and restartEdit and restartRebuild the executable (about 10 s)
Best forLight extensions, trying things outMany or heavy third-party extensionsExtensions you use every day

For most extensions the interpreter makes no visible difference: they spend their time waiting on the model, files or processes. It matters for extensions that do heavy work in JavaScript. See Choose a build for measurements.

Extensions that need the JIT build#

  • Extensions that call native libraries through bun:ffi fail on the default build with bun:ffi requires the JIT. An example is pi-fff, which replaces Pi's find and grep with FFF, a fast, pre-indexed file search in Rust.
  • Extensions that run a lot of code they generate with eval, new Function or vm: that code is only ever interpreted on the default build.
  • Extensions that do heavy work in JavaScript: they work on the default build, more slowly.

Install the JIT build with PIBOLT_VARIANT, as described in Switch builds.

Extensions that start Pi#

Some extensions start more Pi processes, for example to run subagents. If such an extension runs the pi command, its subagents run on stock Pi (or fail when Pi is not installed). Check the extension's settings for the command it runs. For pi-subagents, set:

export PI_SUBAGENT_PI_BINARY=pi-bolt

OpenSec extensions#

The installer offers three extensions that OpenSec maintains for Pi-Bolt. They are prebuilt for Pi-Bolt's runtime, so they load without being transpiled; they are in Pi-Bolt's package list only, as Pi cannot run them. The installer installs the versions each release pins.

PackageWhat it does
opensec-pi-subagentsSpecialized agents in separate sessions that inherit the parent's model and thinking level, with parallel workflows and scheduled jobs.
opensec-pi-todoA todo list for the model, shown as a live panel above the editor. It survives /reload and compaction.
opensec-pi-fffFast file and content search: find, grep and @-mentions, as fast on the default build as with the JIT. Not offered when @ff-labs/pi-fff is installed.
$ pi-bolt install npm:opensec-pi-subagents
$ pi-bolt install npm:opensec-pi-todo
$ pi-bolt install npm:opensec-pi-fff

Compile extensions in#

An extension compiled into the executable runs as machine code like Pi itself, with no startup cost. It needs a Pi-Bolt build environment (see Building).

  1. Write a manifest

    A module whose default export lists the extensions' factories:

    import type { InlineExtension } from "@earendil-works/pi-coding-agent";
    import gitGuard from "./extensions/git-guard.ts";
    import wordCount from "./extensions/word-count.ts";
    
    const plugins: InlineExtension[] = [
    	{ name: "git-guard", factory: gitGuard },
    	{ name: "word-count", factory: wordCount },
    ];
    
    export default plugins;

    Everything in the manifest's folder is built with it, including its node_modules (run npm install there first).

  2. Build
    $ scripts/build-pi.sh --plugins my-plugins/plugins.ts --out out/pi-bolt-plugins

    On Windows: scripts\build-pi.ps1 -Plugins my-plugins\plugins.ts -Out out\pi-bolt-plugins.

  3. Check
    $ BUN_STATIC_HEAP_VERBOSE=1 out/pi-bolt-plugins/pi --version 2>&1 | grep "image registered"
    [aot] image registered: true

    A named extension is a built-in, builtin:<name>: -builtin:<name> in the extensions setting turns it off. If the same extension is also installed at run time, the run-time copy takes over and the compiled-in one steps aside.

The full guide, with workers, embedded files and training a profile, is Porting Pi plugins to Pi-Bolt.

Compatibility checklist#

These are the rules for any code bundled into one executable. Most extensions already follow them.

  • Import Pi from its packages (@earendil-works/pi-coding-agent, @earendil-works/pi-ai, @earendil-works/pi-tui, typebox) and list them as peerDependencies, so that no second copy is bundled.
  • Use static imports. An import of a path computed at run time cannot be bundled. Use a table of literal imports instead.
  • Import files instead of reading them next to the source: import prompt from "./prompt.md" with { type: "text" }.
  • Keep the factory light. Start processes, sockets and timers from session_start or the command that needs them.
  • Do not replace the standard objects' methods. Pi-Bolt freezes them, so Array.prototype.map = ... throws. Polyfills that only add what is missing are fine.

Security#

Extensions run inside the Pi process with your permissions, whether they are loaded at run time or compiled in. Only install extensions from sources you trust.