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 build | Loaded at run time, JIT build | Compiled in | |
|---|---|---|---|
| Changes needed | None | None | Usually none (see the checklist) |
| Startup cost | TypeScript compiled at each start | TypeScript compiled at each start | None |
| Hot code runs as | Bytecode interpreter | Interpreter, then JIT | Machine code |
| Update the extension | Edit and restart | Edit and restart | Rebuild the executable (about 10 s) |
| Best for | Light extensions, trying things out | Many or heavy third-party extensions | Extensions 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:ffifail on the default build withbun:ffi requires the JIT. An example is pi-fff, which replaces Pi'sfindandgrepwith FFF, a fast, pre-indexed file search in Rust. - Extensions that run a lot of code they generate with
eval,new Functionorvm: 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.
| Package | What it does |
|---|---|
opensec-pi-subagents | Specialized agents in separate sessions that inherit the parent's model and thinking level, with parallel workflows and scheduled jobs. |
opensec-pi-todo | A todo list for the model, shown as a live panel above the editor. It survives /reload and compaction. |
opensec-pi-fff | Fast 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).
-
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(runnpm installthere first). -
Build
$ scripts/build-pi.sh --plugins my-plugins/plugins.ts --out out/pi-bolt-pluginsOn Windows:
scripts\build-pi.ps1 -Plugins my-plugins\plugins.ts -Out out\pi-bolt-plugins. -
Check
$ BUN_STATIC_HEAP_VERBOSE=1 out/pi-bolt-plugins/pi --version 2>&1 | grep "image registered" [aot] image registered: trueA named extension is a built-in,
builtin:<name>:-builtin:<name>in theextensionssetting 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 aspeerDependencies, 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_startor 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.