Skip to content
Back to skills

Create Plugin

ASecurity

Create an OpenCode plugin for iPolloWork. Scaffolds the plugin file with the correct API shape, tool definitions, and hook registration. Use when the user asks to 'create a plugin', 'write a plugin', or 'make a plugin that does X'.

  • 6,639 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 2, 2026
ai-agentstypescriptreactnodeawsapi

Works with

  • api
  • mcp

Security analysis

A100/100

Scanned September 2, 2026

npx -y skills add Devin-AXIS/iPolloWork --skill create-plugin --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Create Plugin?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Create Plugin
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/devin-axis-create-plugin/badge)](https://www.skillsdirectory.com/skills/devin-axis-create-plugin)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: create-plugin
description: "Create an OpenCode plugin for iPolloWork. Scaffolds the plugin file with the correct API shape, tool definitions, and hook registration. Use when the user asks to 'create a plugin', 'write a plugin', or 'make a plugin that does X'."
---

# Skill: Create an OpenCode Plugin

Scaffold a working OpenCode plugin for use in iPolloWork.

## When to use

- User asks "create a plugin that does X"
- User asks "write a plugin" or "make a plugin"
- User wants to extend iPolloWork/OpenCode with custom tools

## Plugin API

An OpenCode plugin is an **async factory function** that returns a **hooks object**.

### File location

Plugins can live in:
- **Project**: `.opencode/plugins/my-plugin.ts` (auto-discovered)
- **Global**: `~/.config/opencode/plugins/my-plugin.ts`
- **npm**: published as a package, referenced in `opencode.json` `plugin` array
- **URL**: `file:` or `https:` path in the `plugin` array

### Minimal shape

```typescript
import { z } from "zod";

export default async () => ({
  tool: {
    my_tool_name: {
      description: "What this tool does.",
      args: z.object({
        input: z.string().describe("The input parameter."),
      }).shape,   // NOTE: .shape, not the ZodObject itself
      async execute(args: { input: string }) {
        // Your logic here. Can use fetch(), fs, child_process, etc.
        return `Result: ${args.input}`;
      },
    },
  },
});
```

### Key rules

1. **Export default an async function** that returns the hooks object.
2. **Tool args use `zodSchema.shape`** (a `ZodRawShape`), not the `ZodObject`.
3. **`execute` returns a `string`** or `{ output: string; metadata?: Record<string, unknown> }`.
4. **Tools are declared, not registered imperatively** — return them in the hooks object.
5. **`fetch()` works** — plugins run in-process inside the OpenCode runtime.
6. **`process.env` is accessible** — use env vars for secrets/config.

### Available hooks

```typescript
{
  // Modify the system prompt
  "experimental.chat.system.transform": async (input, output: { system: string[] }) => {
    output.system.push("Extra instruction for the agent.");
  },

  // Define tools the agent can call
  tool: {
    tool_name: { description, args, execute },
  },

  // Run code before/after a tool executes
  "tool.execute.before": async ({ tool, args }) => { /* ... */ },
  "tool.execute.after": async ({ tool, args, result }) => { /* ... */ },

  // React to lifecycle events
  event: async ({ event }) => { /* ... */ },
}
```

### Registering the plugin

Add to `opencode.json`:
```json
{
  "plugin": [
    ".opencode/plugins/my-plugin.ts"
  ]
}
```

Or install from npm:
```json
{
  "plugin": [
    "my-published-plugin"
  ]
}
```

## Anthropic / Claude plugin compatibility

OpenCode plugins are **NOT** the same as Anthropic's plugin format. Key differences:

| Aspect | OpenCode Plugin | Anthropic Plugin |
|--------|----------------|-----------------|
| Entry point | Async factory function | Manifest JSON |
| Tool args | Zod schema `.shape` | JSON Schema |
| Runtime | In-process (Bun/Node) | Sandboxed container |
| Auth | `process.env` | OAuth/API key in manifest |
| Distribution | npm / file path / URL | Anthropic marketplace |

To adapt an Anthropic plugin for OpenCode:
1. Rewrite the tool definitions using Zod instead of JSON Schema.
2. Move any auth from the manifest to env vars or `opencode.json` provider config.
3. Export the async factory function instead of the manifest.
4. HTTP-based Anthropic plugins can often be wrapped as MCP servers instead.

## Example: plugin with multiple tools

```typescript
import { z } from "zod";

export default async () => ({
  "experimental.chat.system.transform": async (_input: unknown, output: { system: string[] }) => {
    output.system.push("You have access to a note-taking system. Use save_note and list_notes.");
  },
  tool: {
    save_note: {
      description: "Save a note with a title and body.",
      args: z.object({
        title: z.string().describe("Note title"),
        body: z.string().describe("Note content"),
      }).shape,
      async execute(args: { title: string; body: string }) {
        const fs = await import("node:fs/promises");
        const path = `.opencode/notes/${args.title.replace(/[^a-zA-Z0-9-_]/g, "_")}.md`;
        await fs.mkdir(".opencode/notes", { recursive: true });
        await fs.writeFile(path, `# ${args.title}\n\n${args.body}\n`);
        return `Saved note: ${path}`;
      },
    },
    list_notes: {
      description: "List all saved notes.",
      args: {},
      async execute() {
        const fs = await import("node:fs/promises");
        try {
          const files = await fs.readdir(".opencode/notes");
          return files.filter(f => f.endsWith(".md")).join("\n") || "No notes yet.";
        } catch {
          return "No notes yet.";
        }
      },
    },
  },
});
```

## Scaffold workflow

When the user describes what they want the plugin to do:

1. Create `.opencode/plugins/<name>.ts` with the plugin code.
2. Add it to `opencode.json` `plugin` array if not already present.
3. Tell the user to restart their session for the plugin to load.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.