Guides · Extensions
Adapt a Pi package
Preserve portable Agent Skills directly, translate Pi runtime registrations to Extension API v1, compile before packaging, and make every semantic difference and trust decision explicit.
Start from observable behavior
Compatibility is a reviewed adaptation, not a promise that Autohand can import arbitrary Pi TypeScript. Write down what users can observe: skill invocation, commands, tool results, dialogs, status content, shortcuts, flags, hooks, model behavior, and permission decisions.
Good target: “Preserve $release-workflow, /release, the release picker, Ctrl+R, and the provider-backed summary; document that Autohand uses its own modal lifecycle and permission ordering.”
Invoke the bundled authoring skill with the source path and desired outcome:
Inspect without executing the package
Read package.json, every pi.extensions and pi.skills path, referenced resources, local dependencies, build scripts, and lockfiles as data. Do not import the package or run its lifecycle scripts merely to discover registrations.
{
"name": "pi-release-helper",
"pi": {
"extensions": ["./extensions/index.ts"],
"skills": ["./skills/release-workflow/SKILL.md"]
}
}Record every external dependency and side effect before choosing the destination. Network calls, process spawning, filesystem mutation, credential reads, and native modules need an explicit operating and trust story.
Classify each contribution
| Pi behavior | Autohand destination | Trust |
|---|---|---|
SKILL.md instructions and references | contributes.skills | Declarative; review content and paths |
| Bounded shell-backed tool | contributes.tools | Declarative; normal invocation authorization |
| Focused sub-agent prompt | contributes.agents | Declarative; tool list is not a permission grant |
registerCommand | api.commands.register | Trusted runtime |
| Pi UI, renderer, menu, dialog, or editor | api.ui.registerView with host React and Ink | Trusted runtime |
| Event handler | api.hooks.on | Trusted runtime |
| Shortcut or startup flag | api.keybindings.register or api.cli.registerFlag | Trusted runtime |
| Model provider | api.providers.register plus extensionProviders config | Trusted runtime |
| Permission behavior | Declarative tools plus api.permissions.registerPolicy | Trusted policy cannot override immutable security |
| Long-running service or isolated integration | MCP server or Agent SDK host | Separate process boundary |
If a bounded declarative contribution preserves the behavior, prefer it. Use the runtime layer only for behavior that genuinely belongs inside the Autohand process.
Port the Agent Skill first
Copy the complete skill directory, not only its entrypoint, and declare the SKILL.md path:
{
"schemaVersion": 1,
"extensionApi": 1,
"id": "company.release-helper",
"name": "Release Helper",
"version": "1.0.0",
"description": "Prepare evidence-backed releases.",
"contributes": {
"skills": ["skills/release-workflow/SKILL.md"]
}
}Update host-specific command names, tool names, filesystem assumptions, and examples. Keep references relative to the skill directory. After installation, daily use is $release-workflow; $extension-builder is only for authoring and repair.
Translate runtime registrations
Write an Autohand entrypoint instead of wrapping or evaluating Pi source. Preserve intent while using Autohand's host-owned React, Ink, command routing, and cleanup.
export function activate(api) {
const { React, Ink } = api.ui;
function ReleasePicker({ close }) {
Ink.useInput((_input, key) => {
if (key.return) close('release plan selected');
});
return React.createElement(
Ink.Text,
{ color: 'cyan' },
'Press Enter to build the release plan',
);
}
api.ui.registerView({
id: 'company.release-helper.picker',
title: 'Release picker',
component: ReleasePicker,
});
api.commands.register({
command: '/release',
description: 'Open the release workflow',
execute(context) {
return context.ui.open('company.release-helper.picker');
},
});
api.keybindings.register({
key: 'ctrl+r',
command: '/release',
when: 'input-empty',
});
}Autohand owns modal pause/resume, alternate-screen cleanup, Escape, and Ctrl+C behavior. Do not bundle a second React or Ink copy. Commands, views, flags, providers, segment ids, and shortcuts must not collide with built-ins or other extensions.
Compile and declare the runtime
Autohand loads compiled JavaScript only. Use your existing build tool to emit a self-contained runtime file and declare that output, not the TypeScript source.
npm run build
# Review the emitted entrypoint and bundled dependency notices.
sed -n '1,260p' dist/extension.mjs{
"schemaVersion": 1,
"extensionApi": 1,
"id": "company.release-helper",
"name": "Release Helper",
"version": "1.0.0",
"description": "Prepare evidence-backed releases.",
"contributes": {
"skills": ["skills/release-workflow/SKILL.md"],
"runtime": ["dist/extension.mjs"]
}
}Do not rely on postinstall, dynamic TypeScript transpilation, or undeclared files. A copied install must remain functional without the original development checkout.
Document semantic differences
Create a short mapping table in the extension README. Call out intentionally changed behavior instead of implying byte-for-byte compatibility.
| Original | Adapted behavior | Reason |
|---|---|---|
| Pi skill | Same $release-workflow name and referenced rubric | Portable Agent Skill contract |
| Pi command | /release registered through Autohand routing | Built-in commands remain reserved |
| Pi custom UI | Host React 19 + Ink 7 view | Avoid duplicate renderers and preserve terminal cleanup |
| Pi permission callback | Autohand policy and normal approval path | Immutable security and session mode remain authoritative |
Validate, trust, and prove lifecycle
autohand extensions validate ./company.release-helper
autohand extensions install ./company.release-helper --link --trust
autohand extensions show company.release-helper
autohand extensions doctor
# Start a fresh CLI and exercise $release-workflow, /release, and Ctrl+R.
autohand extensions disable company.release-helper
autohand extensions enable company.release-helper
autohand extensions remove company.release-helper --yes
# Release proof uses a copied install:
autohand extensions install ./company.release-helper --trust- Save validation JSON and a deliberately broken-package failure.
- Exercise every skill, tool, agent, command, view, shortcut, flag, hook, provider, and policy under normal permissions.
- Prove a denied tool call cannot be bypassed.
- Use a real PTY/Tuistory test for modal and keyboard behavior.
- Confirm disable removes all registrations and enable restores them once.
- Verify a fresh copied install from an immutable checkout before publishing.
Never ask users to trust code they cannot review. Publish source, emitted runtime, dependency notices, build instructions, and an immutable release tag together.