This repo ships Pi extensions under extensions/. Each extension lives in its own subdirectory with an index.ts entry point and a README.md:
extensions/
├── web-distill/ # overrides fetch_content + web_search
│ ├── index.ts
│ ├── bin/web-distill # the Python script it shells out to
│ └── README.md
├── cortecs/ # registers the cortecs provider
│ ├── index.ts
│ └── README.md
├── jira/ # bridges Jira lifecycle hooks into Pi events
│ ├── index.ts
│ └── README.md
└── slack/ # read-only Slack tools (list/history/threads/users/search)
├── index.ts
└── README.md
Pi auto-discovers both layouts under extensions/: loose *.ts files and */index.ts subdirectory entries.
The repo's package.json declares the pi manifest:
{
"name": "data-agent-lab",
"keywords": ["pi-package"],
"pi": {
"extensions": ["./extensions"],
"skills": ["./skills"],
"prompts": ["./prompts"]
}
}When you install this repo as a Pi package, Pi reads the pi.extensions glob and loads every index.ts (and *.ts) it finds under extensions/. TypeScript is loaded via jiti — no compile step, edits are picked up on /reload.
Pi discovers extensions from several locations in parallel. If the same extension exists in two places, both copies load and both try to register/override the same tool, which produces "duplicate tool" errors. You must keep each extension in exactly one place.
The migration is three steps:
pi install /home/arens/projects/data-agent-labThis appends the repo path to packages in ~/.pi/agent/settings.json:
{
"packages": [
"npm:pi-subagents",
"npm:pi-mcp-adapter",
"npm:pi-lens",
"/home/arens/projects/data-agent-lab"
]
}Local paths are added without copying — Pi reads from the working tree, so git pull is enough to get updates.
rm ~/.pi/agent/extensions/web-distill.ts
rm -rf ~/.pi/agent/extensions/cortecsweb-distill overrides fetch_content / web_search, and deleting it mid-session can leave the running agent without those tools until restart.
piIn the new session, verify both extensions loaded:
/model # cortecs provider + its models should be listed
And fetch_content / web_search now come from extensions/web-distill/index.ts in the repo.
Once the repo is installed as a package:
- Edit
extensions/<name>/index.tsin the repo. - In Pi, run
/reload. - Pi re-reads the package manifest and hot-reloads the TypeScript via jiti.
No rebuild, no reinstall. The bundled Python binary (extensions/web-distill/bin/web-distill) is a plain script — edit it directly, changes apply on the next tool call.
-
Create
extensions/<name>/index.tsexporting a default factory:import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; export default function (pi: ExtensionAPI) { pi.registerTool({ /* ... */ }); }
-
Add
extensions/<name>/README.mddocumenting what it does, its requirements, and how to configure it. -
/reloadin Pi — the new extension is picked up automatically.
See the Pi extensions docs for the full API (registerTool, registerCommand, on(event, …), registerProvider, …).
Because the repo has keywords: ["pi-package"] and a pi manifest, it can also be installed from a remote:
pi install git:github.com/sipgate/data-agent-lab
# or after publishing to npm:
pi install npm:data-agent-labFor private use, the local path install above is enough.