Plugin Development

OMP plugins extend the harness with new commands, skills, agent tools, and UI panels. A plugin is a directory with a manifest and one or more entry-point modules.

Plugin Structure

my-plugin/
  plugin.json          # manifest — id, version, hooks, skills
  src/
    index.ts           # main entry — exports activate() + deactivate()
    skills/
      my-skill.md      # skill instruction file
    tools/
      my-tool.ts       # agent tool definition
  tests/
    my-plugin.test.ts  # integration tests
  README.md

The manifest plugin.json is the only required file. Entry point defaults to src/index.ts.

Manifest Reference

{
  "id": "my-plugin",
  "name": "My Plugin",
  "version": "1.0.0",
  "description": "What this plugin does",
  "author": "you@example.com",
  "entry": "src/index.ts",
  "hooks": ["session:start", "tool:before", "tool:after"],
  "skills": ["src/skills/my-skill.md"],
  "tools": ["src/tools/my-tool.ts"],
  "permissions": ["fs:read", "network"]
}

Lifecycle Hooks

session:start

Runs when an OMP session initialises. Use for setup, env checks, or injecting system prompts.

session:end

Runs after the session completes. Ideal for cleanup, logging, or flushing telemetry.

tool:before

Intercepts a tool call before execution. Return false to abort; mutate args to override inputs.

tool:after

Receives the tool result. Can mutate the result or trigger side-effects.

agent:spawn

Fires when a sub-agent is created. Inject context or constraints before the agent runs.

message:output

Intercepts every assistant turn. Use for streaming logging or output post-processing.

Writing a Skill

Skills are Markdown instruction files discovered via the skills field in plugin.json. The frontmatter sets the trigger and metadata:

---
name: my-skill
trigger: "my keyword"
description: "What this skill does"
---

# My Skill

Instructions for the model go here. Use imperative tone.
Reference other skills with `skill://other-skill`.

Registering a Tool

// src/tools/my-tool.ts
import type { ToolDefinition } from '@omp/sdk';

export const myTool: ToolDefinition = {
  name: 'my_tool',
  description: 'Does a specific task',
  inputSchema: {
    type: 'object',
    properties: {
      target: { type: 'string', description: 'The target resource' },
    },
    required: ['target'],
  },
  async execute({ target }) {
    // implementation
    return { result: `processed ${target}` };
  },
};

Testing Plugins

  • Use @omp/test-utils to spin up a sandboxed harness session.
  • Call createTestSession({ plugins: [myPlugin] }) to mount your plugin.
  • Assert hook calls with the built-in spy helpers; assert tool results directly.
  • Run bun test — OMP tests run in an isolated Bun subprocess to avoid state leakage.
  • E2E tests should cover at least one full session lifecycle (start → tool call → end).

Publishing

Open a PR to oh-my-pi/plugins registry with your plugin directory. Include a README.md with install instructions, a changelog, and test evidence. The registry bot will run CI and add the plugin to the discovery index on merge.