Example

Ship an extension, skills, and prompts as one Pi package

Walk an install, filter, and remove cycle, and keep host-provided packages in peerDependencies so the host does not warn.

A Pi package is the distribution unit for everything you have written: an extension, its skills, prompts and themes arrive as one directory or npm package instead of a zip of a .pi folder that breaks on someone else’s machine. There is no standalone code file for this chapter, so this page is a command walkthrough with placeholder names, grounded in what the shipped-binary test asserts.

Install, list, remove

pi install npm:@example/pi-tools@1.0.0              # pinned npm version
pi install git:github.com/example/pi-tools@v1       # cloned, reconciled to the ref
pi install ./local-package                          # loaded from its path; nothing copied
pi list                                             # what resolves, as one instance
pi remove <source>                                  # and its resources stop loading
pi -e npm:@example/pi-tools                         # try one invocation; no settings write
pi install --local ./local-package                  # writes the project's .pi/settings.json

A local install records a path relative to the settings file and copies nothing; --local writes under the project’s .pi and loads only after project trust is granted. Identity by resolved path — a local package by its absolute path, a git package by repository URL without the ref — means the same package listed twice loads once.

Filter what loads

Settings can narrow a package per resource type. [] means none, !pattern excludes, and a filter can never expose a resource the package did not declare:

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

The dependency rule

Pi supplies five packages to your extension code, and they must never be bundled — a physical copy bypasses the module mapping and creates duplicate classes, registries and initialization. Declare them as peer dependencies with a * range:

{
  "name": "pi-guard",
  "version": "0.3.0",
  "keywords": ["pi-package"],
  "peerDependencies": { "@earendil-works/pi-coding-agent": "*", "@earendil-works/pi-ai": "*" },
  "dependencies": { "fast-glob": "^3.3.2" },
  "pi": { "extensions": ["./src/extension.ts"] }
}

The same five in dependencies draw the warning in the table below.

Expected outcome

Command / declaration Behaviour (observed via the shipped binary)
pi install ./my-pkg Records ../my-pkg in the user settings, relative to the settings file; copies nothing
pi list / pi remove Shows it; removes it; the template stops expanding
No manifest extensions/, skills/, prompts/ discovered by convention; the extension’s factory ran once
pi -e ./my-pkg Loads for one invocation; writes nothing to settings
"prompts": [] filter No prompts load; the extension still loads
Same package listed twice Loads once — identity is the resolved path
pi install --local Written to the project’s .pi/settings.json; loads only after trust is granted
Host package in dependencies Warning; with peerDependencies "*" there is none

Mechanism and limitations

Observed: packages.test.ts drives the shipped pi binary against a real package directory with a scripted model and asserts the table above. Documented: packages.md pins versioned git refs (updates reconcile the checkout but do not move a configured ref), and states “Filters narrow the package manifest. They do not expose resources that the package itself did not declare.”

This example runs nothing for you and shows no model. Git and npm sources, pi update, and autoload: false were not run by the test. A package can execute extension code and instruct the model to run programs, so review third-party source before installing — the entry is a gate on executable content, not a bookmark.

Full source and test .

Understand this example

Copy this prompt into your AI tool. No code runs here.

Apply this example

Copy this prompt into your AI tool. No code runs here.