pi-mono/packages/coding-agent/docs/packages.md

8 KiB
Raw Permalink Blame History

Pi Packages

Pi packages install and distribute extensions, skills, prompt templates, and themes as one unit. Use a package when a customization should be shared through npm or git, or when several resources belong together.

A package is an ordinary directory or npm package. It can expose conventional resource directories, declare explicit paths under the pi key in package.json, and carry its own runtime dependencies.

Install and manage packages

Install from npm, git, or a local path:

pi install npm:@example/pi-tools@1.0.0
pi install git:github.com/example/pi-tools@v1
pi install ./local-package

pi list shows configured packages. Use pi remove <source> to remove one and pi update --extensions to reconcile package installations. See Command Line for every package command and option.

If a configured package fails to install at startup, for example because npm cannot reach the registry, Pi warns about it, skips that package's resources, and keeps running. Pi tries to install it again on the next start or /reload. pi install and pi update --extensions still report the failure and exit non-zero.

Personal installs are written to ~/.pi/agent/settings.json. Add --local or -l to write the package declaration to .pi/settings.json. Pi reads declarations from that file only after project trust is granted.

Project packages are installed and loaded only after project trust is resolved. Packages can execute extension code and can include skills that instruct the model to run programs. Review third-party package source before installing it. Review project package declarations before granting project trust.

Use --extension or -e to try a package for one invocation without adding it to settings:

pi -e npm:@example/pi-tools

Pi caches -e packages under ~/.pi/agent/tmp/extensions/ and checks them on every start and /reload. An npm package is reinstalled when the registry has a newer version that matches the specification. A version pinned to an exact number, such as @1.2.3, is not checked. A git package is fetched and reset to the remote branch or ref. A ref that is a commit hash already checked out is not fetched. If the check fails or offline mode is on, Pi loads the cached copy.

Choose a source

Source Example Behavior
npm npm:@example/pi-tools@1.0.0 Installed under the Pi npm directory
git git:github.com/example/pi-tools@v1 Cloned and reconciled to the selected ref
URL https://github.com/example/pi-tools Treated as a git source
Local ./pi-tools Loaded from the resolved path without copying

Versioned npm specifications are pinned. Git tags and commits are also pinned; package updates reconcile the checkout but do not move a configured ref.

Relative local paths resolve from the settings file that contains them. A file path loads one extension. A directory follows normal package discovery rules.

Create a package

The simplest package uses conventional directories:

my-pi-package/
├── package.json
├── extensions/
├── skills/
├── prompts/
└── themes/

Without a pi manifest, Pi discovers TypeScript and JavaScript extensions, skill directories, Markdown prompts, and JSON themes from those directories.

Use an explicit manifest when resources live elsewhere or need filtering:

{
  "name": "my-pi-package",
  "keywords": ["pi-package"],
  "pi": {
    "extensions": ["./src/extension.ts"],
    "skills": ["./resources/skills"],
    "prompts": ["./resources/prompts/*.md"],
    "themes": ["./resources/themes/*.json"]
  }
}

Paths are relative to the package root. Arrays accept glob patterns and exclusions. List dot-prefixed or symlinked resource roots directly when traversal through a glob would not discover them.

The pi-package keyword makes an npm package eligible for discovery in the Pi package gallery. Optional pi.image and pi.video fields add gallery previews.

Show a changelog

After a package update, Pi can show what changed. On the next fresh launch, Pi reads the package changelog and shows entries newer than the last seen version under "Extension Updates", the same way it shows its own changelog. The first time Pi sees a package, it records the version without showing anything.

This applies to packages that load at least one extension. Packages that only provide skills, prompt templates, or themes do not show a changelog.

By default Pi reads CHANGELOG.md from the package root. Point pi.changelogPath at another file inside the package, or set it to "" to disable the changelog:

{
  "name": "my-pi-package",
  "version": "1.2.0",
  "pi": {
    "changelogPath": "./docs/CHANGELOG.md"
  }
}

Use version headings in the same format as Pi's changelog, such as ## [1.2.0] or ## 1.2.0 - 2026-01-01. Entries are matched against the version field in package.json, so a git package only shows a changelog when that field increases. The collapseChangelog setting condenses the section to one line per package, such as Updated my-pi-package to v1.2.0. Unlike Pi's own changelog, /changelog does not show package changelogs, so read the package's changelog file or repository for details.

Declare dependencies

Put runtime packages imported by extensions in dependencies. Pi installs package dependencies when it installs an npm or git source.

Pi supplies these packages to extensions and skills:

  • @earendil-works/pi-ai
  • @earendil-works/pi-agent-core
  • @earendil-works/pi-coding-agent
  • @earendil-works/pi-tui
  • typebox

Declare the host-provided packages listed above in peerDependencies with a "*" range and do not bundle them. Pi suppresses automatic peer installation for managed npm packages and git packages installed with npm, pnpm, or Bun. Local packages are not installed or modified, so their dependency tree remains the package author's responsibility.

Do not list host-provided packages in dependencies. A physical copy can bypass Pi's extension module mapping in compiled ESM and create duplicate classes, registries, and initialization work. Pi reports an extension warning when it detects this manifest configuration. Other Pi packages used as dependencies must be included in the published tarball and referenced through their node_modules resource paths.

Installed packages load with separate module roots. Do not rely on two packages sharing one dependency instance or one package resolving another package’s undeclared dependency.

Select package resources

The object form in settings narrows which resources load from a package:

{
  "packages": [
    {
      "source": "npm:@example/pi-tools",
      "extensions": ["extensions/*.ts", "!extensions/legacy.ts"],
      "skills": [],
      "prompts": ["prompts/review.md"]
    }
  ]
}

For each resource type:

  • Omit the property to load everything allowed by the package.
  • Use [] to load none of that type.
  • Use !pattern to exclude glob matches.
  • Use +path to include one exact allowed path.
  • Use -path to exclude one exact path.

Filters narrow the package manifest. They do not expose resources that the package itself did not declare.

Run pi config to enable or disable discovered resources and pi's built-in extensions. It starts with personal configuration; press Tab to switch scope, or run pi config --local to start with project overrides.

Understand scope and identity

The same package can appear in personal and project settings. A project entry normally replaces the personal entry. With autoload: false, the project entry instead acts as a filtering delta over the personal package.

Pi identifies npm packages by package name, git packages by repository URL without the ref, and local packages by resolved absolute path. This prevents the same package from loading twice through equivalent declarations.

Use Extensions, Skills, Prompt Templates, and Themes to design each resource before packaging it.