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

> Learn to build and contribute to PI-Desktop plugins. Follow this guide to create, validate, and pack your plugins for distribution with the Plugin DevKit CLI.

- Repository: [Lan/PI-Desktop](https://github.com/vastsa/PI-Desktop)
- Tags: how-to-guide
- Published: 2026-09-12

---

**To build a PI-Desktop plugin, create a folder containing a [`manifest.json`](https://github.com/vastsa/PI-Desktop/blob/main/manifest.json) and [`main.js`](https://github.com/vastsa/PI-Desktop/blob/main/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`](https://github.com/vastsa/PI-Desktop/blob/main/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`](https://github.com/vastsa/PI-Desktop/blob/main/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`](https://github.com/vastsa/PI-Desktop/blob/main/spec/07-plugins/13-plugin-permissions-matrix.md). The [`manifest.json`](https://github.com/vastsa/PI-Desktop/blob/main/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`](https://github.com/vastsa/PI-Desktop/blob/main/manifest.json) declares the plugin's identity, contributions, and required permissions. Refer to [`spec/07-plugins/02-plugin-manifest-schema.md`](https://github.com/vastsa/PI-Desktop/blob/main/spec/07-plugins/02-plugin-manifest-schema.md) for the formal JSON schema.

```json
{
  "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`](https://github.com/vastsa/PI-Desktop/blob/main/main.js) file exports lifecycle hooks (`onLoad` and `onUnload`) that the runtime calls when activating or deactivating the plugin.

```js
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()`.

```html
<!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:

```bash
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`](https://github.com/vastsa/PI-Desktop/blob/main/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:

```bash

# 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`](https://github.com/vastsa/PI-Desktop/blob/main/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`](https://github.com/vastsa/PI-Desktop/blob/main/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`](https://github.com/vastsa/PI-Desktop/blob/main/manifest.json) declaring contributions (commands, panels, agent tools, etc.) and explicit permissions.
- The [`main.js`](https://github.com/vastsa/PI-Desktop/blob/main/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`](https://github.com/vastsa/PI-Desktop/blob/main/manifest.json) file and a [`main.js`](https://github.com/vastsa/PI-Desktop/blob/main/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`](https://github.com/vastsa/PI-Desktop/blob/main/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`](https://github.com/vastsa/PI-Desktop/blob/main/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.