How to Build and Contribute to PI-Desktop Plugins: A Complete Developer Guide

To build a PI-Desktop plugin, create a folder containing a manifest.json and main.js, declare permissions and contributions, then use the Plugin DevKit CLI to validate and pack it into a .piplug file for distribution.

PI-Desktop exposes a full-featured plugin system that lets developers extend the application with custom commands, panels, agent tools, skills, themes, and MCP servers. Whether you want to build and contribute to PI-Desktop plugins for private use or share them with the community, the repository provides a complete development toolchain and strict security architecture. All plugin capabilities are governed by the Plugin Specification defined in spec/07-plugins/ and the runtime API exposed through the global pi object.

Understanding the Plugin Architecture

PI-Desktop uses a multi-process architecture that isolates plugin code from the main application while maintaining rich integration capabilities.

The Plugin Process and Sandbox Model

Every plugin runs in a dedicated Node process that loads the entry script (main.js) specified in the manifest. This process has access to the global pi API for registering contributions such as commands, tools, and services. When a plugin declares UI contributions like panels or views, PI-Desktop launches sandboxed Electron windows that contain only the HTML/CSS/JS assets. These renderer processes communicate with the plugin host through window.pluginBridge, never directly with the Node API.

According to the source code in docs/plugin-development.md, this separation ensures that UI code cannot access the filesystem or network without explicit permission granted through the bridge.

Permission Gateway and Security

Every API call—whether pi.fs.*, pi.net.fetch, or pi.agent.*—passes through a permission gateway defined in spec/07-plugins/13-plugin-permissions-matrix.md. The manifest.json must declare all required permissions in the permissions array (e.g., ui.panel, fs.read, net.fetch). At install time, PI-Desktop prompts the user to grant each permission, and any missing permission causes the corresponding API call to fail with PERMISSION_DENIED.

Creating Your First Plugin

A minimal plugin requires three components: a manifest, an entry script, and optional UI assets.

The Manifest File

The manifest.json declares the plugin's identity, contributions, and required permissions. Refer to spec/07-plugins/02-plugin-manifest-schema.md for the formal JSON schema.

{
  "schemaVersion": 1,
  "id": "local.my-first-plugin",
  "name": "My First Plugin",
  "version": "0.1.0",
  "description": "Opens a panel and shows a greeting.",
  "main": "main.js",
  "ui": {
    "panel": "renderer/index.html",
    "title": "My First Plugin",
    "width": 480,
    "height": 360
  },
  "contributes": {
    "commands": [
      {
        "id": "my-first-plugin.open",
        "title": "My First Plugin: Open Panel",
        "keywords": ["hello", "panel"]
      }
    ]
  },
  "permissions": ["ui.panel"],
  "engines": { "piDesktop": ">=0.1.0" },
  "activationEvents": ["onCommand:my-first-plugin.open", "onStartup"]
}

The Entry Script

The main.js file exports lifecycle hooks (onLoad and onUnload) that the runtime calls when activating or deactivating the plugin.

async function onLoad() {
  await pi.commands.register({
    id: "my-first-plugin.open",
    title: "My First Plugin: Open Panel",
    keywords: ["hello", "panel"],
    run: async () => {
      await pi.ui.openPanel({ title: "My First Plugin" });
      await pi.ui.showToast("Hello from My First Plugin");
    },
  });
}

async function onUnload() {
  await pi.commands.unregister("my-first-plugin.open");
}

module.exports = { onLoad, onUnload };

UI Assets and Renderer Communication

Place HTML files under a renderer/ subfolder. The frontend communicates with the plugin process via window.pluginBridge.invoke().

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My First Plugin</title>
  </head>
  <body>
    <h1>My First Plugin</h1>
    <button id="hello">Show toast</button>
    <script>
      document.getElementById("hello").addEventListener("click", async () => {
        await window.pluginBridge.invoke("ui.showToast", {
          message: "Hello from the panel",
        });
      });
    </script>
  </body>
</html>

Development Workflow and Tooling

The repository includes a Plugin DevKit workspace package (@pi-desktop/plugin-devkit) that provides CLI utilities for scaffolding, validating, and packaging plugins.

Scaffolding and File Structure

After installing repository dependencies (pnpm install), use the CLI to generate a new plugin from a template:

pnpm pi-plugin init panel-basic ../my-first-plugin \
  --id local.my-first-plugin \
  --name "My First Plugin"

This creates the folder structure defined in docs/plugin-development.md, including the manifest, main script, and renderer assets.

Hot Reload and Debugging

PI-Desktop automatically reloads plugins during development after a 300ms debounce. The runtime performs an unload → validate → load cycle whenever it detects file changes in the plugin directory. This allows rapid iteration without restarting the application.

Validation and Packaging

Before distribution, validate your plugin against the schema and permission matrix:


# Verify manifest, file scopes, and size limits

pnpm pi-plugin check ../my-first-plugin

# Create distributable .piplug ZIP

pnpm pi-plugin pack ../my-first-plugin

The check command references spec/07-plugins/02-plugin-manifest-schema.md to ensure all required fields are present and that declared permissions match the actual API usage detected in main.js.

Contributing Plugins to the Repository

To contribute a plugin to the main vastsa/PI-Desktop repository:

  1. Fork the repository and create a new feature branch.
  2. Place your plugin under examples/plugins/<your-plugin> to serve as a reference implementation.
  3. Run validation and packaging steps to ensure CI compliance.
  4. Open a pull request that adds your plugin folder.
  5. Update the marketplace catalog in the pi-desktop-plugins repository if you intend to publish it for public installation.

The examples/plugins/hello directory provides a real-world reference showing view contributions, commands, tools, and skills working together. For end-to-end validation, see scripts/e2e/plugin.mjs, which tests registration, UI rendering, and permission enforcement.

Summary

  • PI-Desktop plugins run in isolated Node processes with sandboxed UI windows, communicating through window.pluginBridge.
  • Every plugin requires a manifest.json declaring contributions (commands, panels, agent tools, etc.) and explicit permissions.
  • The main.js entry script uses the global pi API to register capabilities and exports onLoad/onUnload lifecycle hooks.
  • The Plugin DevKit CLI (pnpm pi-plugin) handles scaffolding, validation against spec/07-plugins/, and packaging into .piplug files.
  • Contributions should be placed in examples/plugins/ and validated before submitting a pull request.

Frequently Asked Questions

What file structure is required for a PI-Desktop plugin?

A valid plugin folder must contain a manifest.json file and a main.js entry script at minimum. UI contributions require additional assets, typically placed in a renderer/ subdirectory. The manifest's main field points to the entry script, while ui.panel or contributes.views reference HTML files relative to the plugin root.

How does the permission system work in PI-Desktop plugins?

Permissions are declared in the manifest.json permissions array (e.g., fs.read, net.fetch, ui.panel). At installation, PI-Desktop prompts the user to grant each permission. The runtime enforces these permissions through a gateway layer; any API call lacking the corresponding permission returns PERMISSION_DENIED. The complete mapping of permissions to APIs is documented in spec/07-plugins/13-plugin-permissions-matrix.md.

Can I contribute my plugin to the official repository?

Yes. Fork the vastsa/PI-Desktop repository, place your plugin in examples/plugins/<your-plugin-name>, and ensure it passes validation via pnpm pi-plugin check. The CI pipeline runs scripts from scripts/e2e/plugin.mjs to verify registration, UI functionality, and permission compliance. After merging, you may submit the plugin to the marketplace catalog in the separate pi-desktop-plugins repository.

What is the difference between panels and work-panel views?

Panels are standalone windows defined in the ui.panel manifest section, opened via pi.ui.openPanel(), and suitable for focused tools or utilities. Work-panel views are embedded contributions declared under contributes.views that integrate directly into PI-Desktop's main workspace areas, allowing plugins to provide persistent content alongside the core interface. Both use the same window.pluginBridge communication mechanism but differ in hosting context and lifecycle management.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →