pi-pack
v0.0.11
Published
pi extension installer
Readme
pi-pack
A packaging system for pi extensions.
pi-pack extensions are configured in config.ts by passing options to the extension’s TypeScript function:
// ~/.pi/agent/extensions/fancy-extension/config.ts
import { extension } from "fancy-extension";
export default extension({
// set configuration here
option1: "value",
option2: (x) => `${x + 1}`,
});
Getting started
Install pi-pack
npm install -g pi-packInstall an extension
pi-pack install git:github.com/richardgill/pi-pack-exampleInstalls into: ~/.pi/agent/extensions/pi-pack-example
Customize the extension by editing its config.ts:
// ~/.pi/agent/extensions/pi-pack-example/config.ts
import { piPackExample } from "pi-pack-example";
export default piPackExample({
// set configuration here
commandName: "pi-pack-example",
message: "This extension was configured from config.ts.",
});Installing pi-pack extensions
# From github repo
pi-pack install "git:github.com/user/repo"
pi-pack install "git:github.com/user/repo@v1" # pin git ref: tag, branch, or commit
pi-pack install "git:github.com/user/mono-repo" --extension "extension-name" # from a github monorepo
# From npm
pi-pack install "npm:foo-bar"
pi-pack install "npm:[email protected]" # pin version
pi-pack install "npm:foo-bar" --as "baz" # install into ~/.pi/agent/extensions/baz
# From local file system
pi-pack install "~/code/my-extension"
pi-pack install "~/code/my-extension-mono-repo" --extension "extension-name"pi-pack uses pnpm under the hood, and supports most of pnpm's package sources.
Upgrading extensions
pi-pack upgrade upgrades installed extensions while preserving your local config.ts config.
pi-pack upgrade # upgrades all pi-pack extensions
pi-pack upgrade "extension-name" # upgrade single extensionBy default, upgrades respect the dependency range recorded in each extension's package.json.
--bump upgrades to the latest available version and rewrites the dependency spec in the extension's package.json:
pi-pack upgrade --bump
pi-pack upgrade "extension-name" --bumpUninstalling extensions
pi-pack uninstall # uninstall pi-pack extensions interactively
pi-pack uninstall files tasks # remove named extensions after confirmation
pi-pack uninstall files --yes # skip confirmationUninstall permanently deletes the extension dirs, including config.ts.
Only pi-pack managed extensions are shown or removed.
What pi-pack install does
pi-pack install "git:github.com/richardgill/pi-pack-example"Installs the extension to:
~/.pi/agent/extensions/pi-pack-example/
├── config.ts
└── package.json// config.ts
import { piPackExample } from "pi-pack-example";
export default piPackExample({
// user makes edits here
commandName: "pi-pack-example",
message: "This extension was configured from config.ts.",
});// package.json
{
"private": true,
"type": "module",
"dependencies": {
"pi-pack-example": "github:richardgill/pi-pack-example"
},
"pi": {
"extensions": ["./config.ts"]
},
"pi-pack": {
"managed": true
}
}Extensions are installed via pnpm.
Building an extension
pi-pack extensions repos are simple npm packages:
- They export a function users will call to configure the extension
- They provide a default config that will be copied into:
~/.pi/agent/extensions/<extension-name>/config.ts - They can bundle pi resources like skills, prompt templates, and themes
Migrating existing pi extensions
pi-pack migrate prints instructions for an AI coding agent to migrate an existing pi extension repo to pi-pack.
Run it from the extension repo:
cd my-preexisting-extension/
pi-pack migrateThe command does not inspect or modify files. It prints both standalone and monorepo migration instructions.
Create an extension
Run pi-pack create to create an extension.
pi-pack create <name> creates a single extension package dir.
Single repo extensions
repo/
├── package.json
└── src/
├── extension.ts
└── default-config.ts// package.json
{
"name": "pi-pack-example",
"type": "module",
"exports": {
".": "./src/extension.ts"
},
"keywords": ["pi-package"],
"pi-pack": {
// copied to user's ~/.pi/agent/extensions/pi-pack-example/config.ts when they install
"default-config": "./src/default-config.ts",
// pi-pack will not prompt user to set up their config.ts if "requires-config-edit": false
"requires-config-edit": true
}
}// ./src/default-config.ts
import { piPackExample } from "pi-pack-example";
export default piPackExample({
commandName: "pi-pack-example",
message: "This extension was configured from config.ts.",
});// ./src/extension.ts
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
export type PiPackExampleOptions = {
commandName?: string;
message?: string;
};
export const piPackExample = (options: PiPackExampleOptions = {}) => {
const commandName = options.commandName ?? "pi-pack-example";
const message = options.message ?? "Hello from pi-pack-example.";
return (pi: ExtensionAPI): void => {
pi.registerCommand(commandName, {
description: "Show the pi-pack example message",
handler: async (_args, ctx) => {
ctx.ui.notify(message, "info");
},
});
};
};Mono repo extensions
One repo which includes multiple extensions
mono-repo/
├── package.json
└── extensions/
├── extension-1/
│ ├── package.json
│ └── src/
│ ├── extension.ts
│ └── default-config.ts
└── extension-2/
├── package.json
└── src/
├── extension.ts
└── default-config.ts// package.json
{
"pi-pack": {
// dir where extensions are kept
"extensions-dir": "extensions"
}
}Adding skills, prompts, and themes
Bundle pi resources next to your extension package and return their paths from resources_discover. See example.
repo/
├── skills/
│ └── pi-pack-example/
│ └── SKILL.md
├── prompts/
│ └── explain-pi-pack-example.md
└── themes/
└── pi-pack-example.json// ./src/extension.ts
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const baseDir = dirname(fileURLToPath(import.meta.url));
const packageRoot = join(baseDir, "..");
pi.on("resources_discover", () => ({
skillPaths: [join(packageRoot, "skills", "pi-pack-example", "SKILL.md")],
promptPaths: [join(packageRoot, "prompts", "explain-pi-pack-example.md")],
themePaths: [join(packageRoot, "themes", "pi-pack-example.json")],
}));Motivation and inspiration
pi-pack is inspired by neovim's packaging ecosystem where plugins are configured directly in lua code with full typing.
pi's extensions are powerful, but the built-in extension installation mechanism means every configurable extension must have it's own config file and validation. pi-pack uses the power of TypeScript to make configuring pi extensions easy.
License
MIT
