| Requirement | Version | Notes |
|---|---|---|
| Node.js | ≥ 22.19.0 | hard requirement inherited from Pi; CI/dev machines tested with 22.22.x |
| npm | ≥ 9 | |
| git | ≥ 2.40 | |
| BitBake / Yocto | optional | absent on dev machines is fine — tools must degrade to structured "not available" reports |
| STM32 hardware | optional | never required for development or evaluation (fixtures only) |
Environment note: development currently happens in WSL against a Windows drive (/mnt/d/...). File I/O is slower there; keep heavy build fixtures out of the repo.
- Pi core packages (
@earendil-works/pi-ai,@earendil-works/pi-agent-core,@earendil-works/pi-coding-agent) andtypeboxare declared as peerDependencies (per Pi's package rules — Pi injects its own copies into extensions at load time) and pinned in devDependencies for typechecking. - Current pin: 0.84.4 everywhere, matching the local Pi source tree used as the API reference (
pi-0.84.4/). When upgrading, re-read docs/pi-architecture.md and update the version notes below. - Never bundle Pi core packages into our dist.
Inspect → Understand → Plan → Implement → Test → Review → Document
- TypeScript strict mode; erasable syntax only (no enums/namespaces/parameter properties) — Pi loads extensions with jiti and Node runs TS as strip-only.
- Tool parameters always get TypeBox schemas; string enums use
StringEnumfrom@earendil-works/pi-ai(notType.Union(Type.Literal(...))— breaks Google's API). - Every external command gets a timeout; every subprocess exit code is handled; errors are thrown (never encoded in
content) per the Pi tool convention. - Dangerous operations only through the Safety Layer; never rely on prompt wording alone.
- Honest status reporting: unimplemented features are labeled "Not implemented"; fixture-only testing is labeled as such.
In an interactive session:
| Command | Effect |
|---|---|
/model |
Model picker; press Ctrl+S inside the picker to save it as the startup default |
/thinking |
Thinking level picker (Ctrl+S to save) |
Ctrl+P |
Cycle between scoped models (/scoped-models edits the list) |
/login, /logout |
Provider auth: subscriptions (Claude Pro/Max, ChatGPT, Copilot, xAI, OpenRouter, …) or API keys |
At startup (pilot forwards runtime flags):
pilot --model glm-5.3 # pick another model from the same provider
pilot --provider openai --model gpt-5 # pick a different provider
pilot --api-key sk-... # one-off key (otherwise: env vars / /login)
pilot --list-models # see everything currently available
pilot --models opencode-go/glm-5.3,anthropic/claude-sonnet-4-5 # Ctrl+P cycling listAdding a new provider:
- Built-in providers (anthropic, openai, google, openrouter, xai, …): export the env var (
ANTHROPIC_API_KEY,OPENAI_API_KEY,GEMINI_API_KEY, …) or run/loginonce. Their model catalogs ship with pi. - Any OpenAI-compatible gateway: add an entry to
~/.pi/agent/models.json(see below). The file reloads every time/modelopens — no restart needed. - Startup defaults live in
~/.pi/agent/settings.json(defaultProvider/defaultModel) or are saved from the/modelpicker with Ctrl+S.
Pi reads ~/.pi/agent/models.json (outside this repo, never commit keys). Example for an OpenAI-compatible gateway:
{
"providers": {
"<provider-id>": {
"name": "My Gateway",
"baseUrl": "https://gateway.example.com/v1",
"api": "openai-completions",
"apiKey": "<literal key or $ENV_VAR>",
"models": [
{
"id": "my-model",
"name": "My Model",
"reasoning": false,
"input": ["text"],
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
"contextWindow": 128000,
"maxTokens": 8192
}
]
}
}
}Then run the e2e checks with node scripts/verify-agent-run.mjs <provider-id> <model-id>.
npm run typecheck # tsc --noEmit (strict)
npm run verify:extension # loads src/extension/index.ts via jiti exactly like Pi does
npm run cli -- --version # run the CLI stub
npm link # exposes the `embedded-pilot` bin locally
pi -e ./src/extension/index.ts # run the extension inside an interactive pi sessionSome distro Node builds (including the current WSL one) are compiled without native TS stripping (ERR_NO_TYPESCRIPT). We therefore do not rely on Node's type stripping anywhere: the bin/embedded-pilot.mjs launcher and scripts/verify-extension-load.mjs are plain JS and load the TypeScript sources through jiti — the exact loader Pi uses for extensions. This works on any Node ≥ 22.19 build.
| Date | Pi version | Notes |
|---|---|---|
| 2026-09-04 | 0.84.4 | Baseline. Extension factory (pi: ExtensionAPI) => void; tools via pi.registerTool(defineTool(...)) with TypeBox schemas; blocking via pi.on("tool_call") → { block, reason }; SDK via createAgentSession(). Package manifest: "pi": { "extensions": [...] }. Details with file/line references in pi-architecture.md. |
Legacy scope @mariozechner/pi-* seen in older articles is not current — the 0.84.x line uses @earendil-works/pi-*.
Conventional commits reflecting the real development sequence, e.g.:
feat: initialize embeddedpilot package
feat: add project detection
feat: add safety policy
test: add evaluation fixtures
docs: add architecture documentation