10. Plugin Developer Experience
1. Goals
Let a developer create and load a local plugin within 10 minutes.
The task-oriented zero-to-one plugin development guide covers the complete author journey. This specification freezes the developer surfaces and their acceptance criteria.
2. Developer path
Create from template (the folder opens as the project)
→ edit manifest / main / panel (hot reload keeps it live)
→ verify in the command palette
→ check
→ pack piplugThe first step has three entry points, all calling the same @pi-desktop/plugin-devkit implementation:
- Plugins page — the overflow menu's "New plugin from template", or the button on the empty state. Picks a template, asks for a folder, writes the files, loads the result as a development plugin, and then opens that folder as the active project so the sources are already in the workspace the agent and the file panel read. If the folder cannot be opened as a project the plugin still stays loaded, and the toast says only that it was created and loaded.
- Agent —
PluginScaffold, in a conversation ("write me a plugin that …"). It writes inside the current workspace, which is already open. - CLI —
pnpm pi-plugin init <template> <dir>.
3. Template types
Official templates, all four available:
panel-basic: panel + commandagent-tool-basic: register a toolskill-pack: skills onlyfull-demo: panel + command + tool + skill + settings
Every template scaffolds a manifest with schemaVersion: 1, a main.js, a README, and only the permissions the template actually uses. Scaffolding refuses to write into a non-empty directory.
Current repo example:
examples/plugins/hello
4. SDK and devkit
@pi-desktop/plugin-sdk is imported by plugin code itself and stays dependency-free and Node-free. It provides:
- manifest types
- permission enums
- API types (
PiPluginHostApi) - manifest validation function
- test helper (mock host)
@pi-desktop/plugin-devkit is tooling, not runtime, and may use Node. It owns scaffold / check / pack and the pi-plugin CLI. All three developer surfaces (CLI, agent tools, plugins page) call it, so a rule enforced once holds everywhere.
5. Local development commands
The CLI is currently delivered as a private workspace package. From a checkout of this repository, install dependencies and build the devkit plus its dependencies once:
# repository setup
pnpm install
pnpm --filter @pi-desktop/plugin-devkit... build
# create from a template
pnpm pi-plugin init full-demo /tmp/my-plugin
# validate manifest and package contents
pnpm pi-plugin check .
# pack
pnpm pi-plugin pack .
# outputs dist/demo.hello-0.1.0.piplugcheck reproduces every rule the installer enforces, so check passing implies install will pass. It reports errors — a missing or unparseable manifest.json, missing main / ui.panel / skill files, a skill path escaping the plugin directory, an unknown permission, a symlink, more than 2000 files, more than 50 MB — and warnings, which do not block: high-risk permissions, permissions declared but never used by the code, contributes.skills without agent.prompt.inject (the skills would be inert), and an empty contributes.
pack writes dist/<id>-<version>.piplug, skipping .git and node_modules exactly as the installer's copy does, and prints the sha256. It runs check first and refuses to pack a plugin with errors. Entries are stored uncompressed (method 0): the installer accepts nothing else, so a .piplug must never be built with zip or another shell tool.
6. Agent tools
Three tools are served from Electron main (host-core never sees them), each resolving its directory argument against the session's workspace root and refusing to escape it:
| Tool | Modes | Effect |
|---|---|---|
PluginCheck | all | Validates a plugin directory; read-only |
PluginScaffold | agent | Writes a template, then loads it as a development plugin |
PluginPack | agent | Validates, then writes dist/<id>-<version>.piplug |
A built-in skill, apps/desktop/resources/skills/plugin-development.md, documents the manifest schema, the permission tiers, the host API surface, and this loop. It activates only when the session workspace looks like plugin development — a plugin manifest.json at the workspace root, or a loaded development plugin inside it — so ordinary sessions pay only for the three tool descriptions. Scaffolding writes a manifest, which turns the full skill on from the next prompt; creating from a template on the plugins page also opens the new folder as the project, so the workspace test passes right away.
7. Hot reload
A plugin loaded from a folder is watched from then on, including across restarts: the folder is picked once, not once per edit.
- Any change under the plugin directory reloads it, debounced 300 ms, so one save burst is one reload.
node_modules,.git,dist,targetand editor scratch files are ignored — a plugin writing into its owndist/must not reload itself forever. - A reload unloads the previous process and runs the plugin again from disk, so a manifest,
main, or skill change all take effect the same way. Panels are re-created from the reloaded contribution. - A reload can never widen permissions. The reload reads the manifest first and compares it against the set approved when the folder was picked; anything new stops the reload with
PERMISSION_DENIEDand a message to load the plugin again so the grant can be reviewed. Removed permissions do take effect immediately — grants follow the manifest downwards, never upwards. - The Plugins page shows a Reload action on development-plugin rows. After a permission-gated hot reload, clicking it explicitly reloads the registered folder with the current manifest and refreshes the permission ceiling used by later file-watch reloads. The action does not require picking the folder again.
- A failed reload (syntax error, invalid manifest) leaves the plugin unloaded but still watched, so the save that fixes it recovers the plugin. The failure is reported as a toast plus a plugin-changed event; the registry row does not currently move to
load_error, because host-core has no RPC for a runtime-side load failure. - Watchers are released on unload, disable, uninstall and quit, and are capped at 16 plugins; past the cap the app logs and edits need a manual reload.
8. Debugging
Implemented today:
- Load and hot-reload failures appear as toasts; persisted load failures also appear on the plugin row.
- Open Settings → Info → Logs and filter records by
pluginIdto inspect lifecycle, host API, tool, service, and bus activity. - The Plugins page shows declared capabilities, permissions, and resident service state. Registered commands can be verified in global search.
Later:
- Dedicated per-plugin log panel with stack-copy affordance
- Dedicated DevTools for the panel
- Mock tool invoker
9. Documentation checklist (developer site / repo docs)
- Quick start
- manifest fields
- Permission reference
- API manual
- Publishing manual (pack/sign)
- Security best practices
10. Quality gate (recommended before publishing)
pi-plugin checkreports no errors- No calls to undeclared permissions
- Has a README
- Has a version changelog
- If it includes a tool: provide parameter examples
11. Acceptance
- A new plugin can be created from a template, from the plugins page, the agent or the CLI
- Development load succeeds
- An edit reloads the plugin without re-picking its folder, and a broken edit recovers on the next save
checkpasses and thepackartifact installs- A declared skill reaches the model when
agent.prompt.injectis granted, and stops reaching it when the permission is revoked