Skip to content
Back to skills

Best Practices Pi Extensions

ASecurity

Evidence-backed standards for creating, reviewing, and hardening Pi extensions. Use when building or changing ~/.pi/agent/extensions or .pi/extensions code, package-style Pi extensions, event handlers, custom tools, TypeBox schemas, input/message/tool hooks, retry guards, final-report guards, or humorous but serious enforcement extensions such as lazy-report-shame-shame-shame.

  • 6 stars
  • 0 votes
  • 1 copy
  • 21 views
  • Added September 11, 2026
developmenttypescriptpythonrustgoshellbashnodegitapisecurity

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Pro scans all 5 files and shows the line behind each finding

Scanned September 11, 2026

npx -y skills add grahama1970/agent-skills --skill best-practices-pi-extensions --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Best Practices Pi Extensions?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Best Practices Pi Extensions
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/grahama1970-best-practices-pi-extensions/badge)](https://www.skillsdirectory.com/skills/grahama1970-best-practices-pi-extensions)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: best-practices-pi-extensions
description: >
  Evidence-backed standards for creating, reviewing, and hardening Pi extensions.
  Use when building or changing ~/.pi/agent/extensions or .pi/extensions code,
  package-style Pi extensions, event handlers, custom tools, TypeBox schemas,
  input/message/tool hooks, retry guards, final-report guards, or humorous but
  serious enforcement extensions such as lazy-report-shame-shame-shame.
triggers:
  - pi extension
  - pi extensions
  - pi package extension
  - message_end extension
  - before_agent_start hook
  - input hook
  - tool_call hook
  - tool_result hook
  - custom pi tool
  - final report guard
  - lazy report shame
  - extension event handler
provides:
  - pi-extension-patterns
  - extension-validation
  - report-guard-design
  - package-extension-standard
composes:
  - unlazy
  - agentic-evals
  - memory
  - brave-search
complies:
  - best-practices-skills
  - best-practices-python
  - best-practices-security
  - typescript-code
taxonomy:
  - developer-tooling
  - validation
  - resilience
disciplines:
  - developer-tooling
  - engineering-standards
---

# Pi Extensions Best Practices

Use this before writing or reviewing Pi extensions. Do not invent an extension
shape until you have read the Pi docs and the installed Nico Bailon extensions.

## Required evidence before implementation

Read the official Pi extension API:

- `/home/graham/.local/share/pi-node/node-v22.23.2-linux-x64/lib/node_modules/@earendil-works/pi-coding-agent/docs/extensions.md`
- `/home/graham/.local/share/pi-node/node-v22.23.2-linux-x64/lib/node_modules/@earendil-works/pi-coding-agent/examples/extensions/`

Apply current `$brave-search` results before finalizing public guidance. The
current search receipts used for this standard are:

- `/tmp/bppe-brave-pi.json`: found `https://pi.dev/docs/latest/extensions`, the
  upstream `earendil-works/pi` `docs/extensions.md`, the raw upstream docs, and
  a mirror that documents `pi.sendUserMessage`.
- `/tmp/bppe-brave-nico.json`: found Nico Bailon's `pi-interactive-shell`,
  `pi-intercom`, and `pi-mcp-adapter` repositories.

Use installed Nico repos as concrete implementation examples:

- `/home/graham/.pi/agent/git/github.com/nicobailon/pi-interactive-shell/`
- `/home/graham/.pi/agent/git/github.com/nicobailon/pi-intercom/`
- `/home/graham/.pi/agent/git/github.com/nicobailon/pi-mcp-adapter/`

## Package and module layout

For distributable extensions, copy Nico's package shape:

- `package.json` has `type: "module"`.
- `package.json` declares `pi.extensions` and, when bundled skills exist,
  `pi.skills`.
- Pi host packages and `typebox` are `peerDependencies`, not vendored copies.
- `scripts.test` exists; TypeScript-heavy extensions also have `typecheck`.
- `files` explicitly lists shipped modules, skills, examples, and README assets.
- `index.ts` registers the extension and delegates nontrivial logic to modules.

Keep modules separated by responsibility:

| Concern | Pattern from Nico extensions |
| --- | --- |
| User-facing registration | `index.ts` |
| Config loading/merge | `config.ts` (`pi-interactive-shell`, `pi-intercom`, `pi-mcp-adapter`) |
| Long-lived sessions | `session-manager.ts`, `runtime-coordinator.ts`, broker client/runtime modules |
| UI overlays/panels | `overlay-component.ts`, `mcp-setup-panel.ts`, `ui/**` |
| Output bounding | `mcp-output-guard.ts`, `tool-result-renderer.ts` |
| Tests | `*.test.ts`, `__tests__/`, `conformance/run.sh` |

Do not hide complex parsing, state transitions, subprocess handling, or output
truncation inside one giant `index.ts`.

## Pi APIs by use case

Use the smallest Pi event/API that owns the behavior:

| Need | Pi event/API |
| --- | --- |
| Add per-turn instruction | `before_agent_start` |
| Rewrite/block user input | `input` |
| Block a dangerous tool call | `tool_call` |
| Modify tool evidence | `tool_result` |
| Reject or replace assistant prose | `message_end` |
| Force another model attempt | `pi.sendUserMessage(..., { deliverAs: "followUp" })` |
| Wake the model after an event | `pi.sendMessage(..., { triggerTurn: true })` |
| Register agent tools | `defineTool` with `Type.Object` schemas |
| Show operator-visible status | `ctx.ui.notify`, `ctx.ui.setStatus`, `ctx.ui.setWidget` |
| Open rich UI | `ctx.ui.custom`, gated by `ctx.hasUI` / `ctx.mode` |
| Clean long-lived resources | `session_shutdown`, `dispose`, abort/teardown handlers |

If a feature can run headless, it must not require `ctx.ui.custom`. Follow
`pi-mcp-adapter`: notify that the interactive panel is unavailable and give a
CLI/config fallback when `ctx.hasUI` is false or the mode cannot show UI.

## Tool schemas and result surfaces

Custom tools are user-visible APIs. Use Nico's `defineTool` + `Type.Object`
style, and keep these rules:

- Every parameter has a bounded schema and description.
- Tool return content is concise; put bulky machine-readable data in `details` or
  a saved artifact.
- Errors are structured and name the next valid action.
- Do not dump arbitrary MCP/provider output into the model context.
- Use an output guard pattern like `guardMcpOutput` in `mcp-output-guard.ts`:
  normalize blocks, bound text, preserve images separately, spill oversized raw
  results, and expose the spill location in details.

## Lifecycle, concurrency, and cleanup

Nico's extensions are long-running because they own PTYs, broker sockets, MCP
server runtimes, OAuth callbacks, monitors, and UI panels. Copy the cleanup
contract even for smaller extensions:

- Register lifecycle handlers such as `session_start` and `session_shutdown`.
- Track ownership/generation so stale async callbacks cannot mutate a newer
  session.
- Provide `dispose` for sessions, overlays, monitors, sockets, and panels.
- Use explicit abort/timeouts for subprocesses and network calls.
- When a background process remains alive, expose a session id and status widget
  rather than hiding it.

## Config and trust boundaries

- Prefer global config under `~/.pi/agent/...` plus project overrides only after
  project trust is established.
- Read config through a dedicated loader, validate types, and use safe defaults.
- Never execute repository text as code merely because a config file names it.
- Treat extensions as full-permission code. Do not patch Pi internals or
  `node_modules` for normal behavior.

## Desperation guards must be deterministic

If the extension exists because agents ignored prose, do not ask the same agent
to judge itself.

- Put the decision in a deterministic checker script.
- The extension calls the checker and uses its exit code.
- The checker prints exact rejection reasons.
- The rejected assistant answer must not remain the accepted final answer.
- Queue a forced retry with checker diagnostics using
  `pi.sendUserMessage(..., { deliverAs: "followUp" })`.
- If a retry fails, queue another retry or hand off to the human with a rejection
  notice; never allow the lazy answer as success.

## Final-report guards

Reject reports that launder failure as progress:

- vague unresolved-work language without exact rows;
- `Committed and pushed`, branch names, SHAs, or hook status used as the result;
- `mostly done`, `partially complete`, `remaining gates`, `open items`, or
  `what remains` without exact gate IDs;
- counts that do not include rows and proof boundaries;
- progress reports with no immutable `goal-drift` boundary.

Require this shape:

```text
Progress:
- VERIFIED: <concrete user-visible change or artifact outcome>
MET: <n>
UNMET: <n>
ABANDONED: <n>
Immutable Goal: <goal-drift status and hash>
- UNMET `Gx`: failed condition: <exact condition>. Next legal command:
  <command/owner>. Receipt: <path>. Proof boundary: <what was and was not proven>.
```

## Executable evaluation is mandatory

This skill is a standard, but it still ships executable gates. Any Pi extension
skill or package must include `$agentic-evals` posture, not just prose.

Minimum fixture requirements:

- `fixtures/agentic_eval.json` version 2.
- `trials >= 2`.
- At least one positive real-world case using a script, skill entrypoint,
  test runner, or live API path.
- At least one negative or adversarial case that proves the checker fails when a
  required rule is removed.
- `capability_claims` and `seams` for any operational capability.
- Explicit proof boundary: what the eval proves and what it does not prove.

The canonical validator for this skill is:

```bash
python3 skills/best-practices-pi-extensions/scripts/check_pi_extension_standard.py \
  --skill-dir skills/best-practices-pi-extensions \
  --alias-dir skills/best-practices-pi-extension
```

Run the agentic eval with:

```bash
skills/agentic-evals/run.sh run skills/best-practices-pi-extensions/fixtures/agentic_eval.json
skills/agentic-evals/run.sh run skills/best-practices-pi-extension/fixtures/agentic_eval.json
```

## Validation checklist before reporting success

1. `best-practices-skills/scripts/validate_skill.py` passes for the canonical
   skill and the alias.
2. `scripts/check_pi_extension_standard.py` passes against the canonical skill
   and alias.
3. `$agentic-evals` fixture passes and includes negative/adversarial cases.
4. If code changed, package tests/typecheck run (`npm test`, `vitest`, `tsx
   --test`, or the extension's actual script).
5. If a Pi extension changed, run a live Pi print-mode or isolated extension-load
   test and read back stdout/stderr.
6. Report the proof boundary; Git metadata and unit tests are supporting
   evidence only.

Files in this skill

  • PROJECT_KNOWLEDGE.md5.5 KB
  • README.md3.7 KB
  • SKILL.md9.3 KB
  • fixtures/agentic_eval.json5.5 KB
  • scripts/check_pi_extension_standard.py9.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.