Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Plugin System Overview

Perry supports native plugins as shared libraries (.dylib/.so). Plugins extend Perry applications with custom hooks, tools, services, and routes.

How It Works

  1. A plugin is a Perry-compiled shared library with activate(api) and deactivate() entry points
  2. The host application loads plugins with loadPlugin(path)
  3. Plugins register hooks, tools, and services via the API handle
  4. The host dispatches events to plugins via emitHook(name, data)
Host Application
    ↓ loadPlugin("./my-plugin.dylib")
    ↓ calls plugin_activate(api_handle)
Plugin
    ↓ api.registerHook("beforeSave", callback)
    ↓ api.registerTool("format", callback)
Host
    ↓ emitHook("beforeSave", data) → plugin callback runs

Quick Example

Plugin (compiled with --output-type dylib)

// my-plugin.ts
export function activate(api: PluginAPI) {
  api.setMetadata("my-plugin", "1.0.0", "A sample plugin");

  api.registerHook("beforeSave", (data) => {
    console.log("About to save:", data);
    return data; // Return modified data (filter mode)
  });

  api.registerTool("greet", (args) => {
    return `Hello, ${args.name}!`;
  });
}

export function deactivate() {
  console.log("Plugin deactivated");
}
perry my-plugin.ts --output-type dylib -o my-plugin.dylib

Host Application

import { loadPlugin, emitHook, invokeTool, listPlugins } from "perry/plugin";

loadPlugin("./my-plugin.dylib");

// List loaded plugins
const plugins = listPlugins();
console.log(plugins); // [{ name: "my-plugin", version: "1.0.0", ... }]

// Emit a hook
const result = emitHook("beforeSave", { content: "..." });

// Invoke a tool
const greeting = invokeTool("greet", { name: "Perry" });
console.log(greeting); // "Hello, Perry!"

Plugin ABI

Plugins must export these symbols:

  • perry_plugin_abi_version() — Returns ABI version (for compatibility checking)
  • plugin_activate(api_handle) — Called when plugin is loaded
  • plugin_deactivate() — Called when plugin is unloaded

Perry generates these automatically from your activate/deactivate exports.

Next Steps