Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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'.
.claude/skills/devin-axis-create-plugin/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 45% | 0% |
| case-03 | ✗→✓ | ▲ Improved | -4% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 28% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 5% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 0% | 0% |
Scaffold a working OpenCode plugin for use in iPolloWork.
An OpenCode plugin is an async factory function that returns a hooks object.
Plugins can live in:
.opencode/plugins/my-plugin.ts (auto-discovered)~/.config/opencode/plugins/my-plugin.tsopencode.json plugin arrayfile: or https: path in the plugin arraytypescriptimport { 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}`; }, }, }, });
zodSchema.shape (a ZodRawShape), not the ZodObject.execute returns a string or { output: string; metadata?: Record<string, unknown> }.fetch() works — plugins run in-process inside the OpenCode runtime.process.env is accessible — use env vars for secrets/config.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 }) => { /* ... */ }, }
Add to opencode.json:
json{ "plugin": [ ".opencode/plugins/my-plugin.ts" ] }
Or install from npm:
json{ "plugin": [ "my-published-plugin" ] }
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:
opencode.json provider config.typescriptimport { 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."; } }, }, }, });
When the user describes what they want the plugin to do:
.opencode/plugins/<name>.ts with the plugin code.opencode.json plugin array if not already present.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 20,225 | 8,095 | -60% | 1 | 1 | 0% | 2,853 | 1,865 | -35% | 0 | 0 | — |
case-02 | fail→pass | 17,866 | 16,271 | -9% | 1 | 1 | 0% | 2,506 | 3,624 | +45% | 0 | 0 | — |
case-03 | fail→pass | 17,819 | 12,204 | -32% | 1 | 1 | 0% | 2,870 | 2,748 | -4% | 0 | 0 | — |
case-04 | fail→pass | 16,819 | 11,949 | -29% | 1 | 1 | 0% | 2,166 | 2,766 | +28% | 0 | 0 | — |
case-05 | pass→pass | 12,712 | 7,907 | -38% | 1 | 1 | 0% | 1,403 | 1,868 | +33% | 0 | 0 | — |
case-06 | fail→pass | 17,401 | 9,029 | -48% | 1 | 1 | 0% | 2,047 | 2,145 | +5% | 0 | 0 | — |
case-07 | fail→pass | 17,404 | 9,378 | -46% | 1 | 1 | 0% | 2,129 | 2,121 | -0% | 0 | 0 | — |
case-08 | fail→pass | 15,056 | 13,129 | -13% | 1 | 1 | 0% | 2,508 | 2,879 | +15% | 0 | 0 | — |
case-09 | fail→pass | 29,031 | 9,544 | -67% | 1 | 1 | 0% | 1,939 | 2,242 | +16% | 0 | 0 | — |
case-10 | fail→pass | 21,756 | 26,031 | +20% | 1 | 1 | 0% | 3,396 | 4,012 | +18% | 0 | 0 | — |
case-11 | pass→pass | 12,822 | 8,864 | -31% | 1 | 1 | 0% | 1,414 | 2,066 | +46% | 0 | 0 | — |
case-12 | pass→pass | 13,288 | 9,609 | -28% | 1 | 1 | 0% | 1,452 | 2,172 | +50% | 0 | 0 | — |
case-13 | fail→pass | 16,016 | 8,224 | -49% | 1 | 1 | 0% | 1,766 | 1,962 | +11% | 0 | 0 | — |
case-14 | fail→pass | 18,580 | 4,558 | -75% | 1 | 1 | 0% | 2,151 | 2,305 | +7% | 0 | 0 | — |
case-15 | pass→pass | 6,981 | 2,378 | -66% | 1 | 1 | 0% | 1,067 | 1,706 | +60% | 0 | 0 | — |
case-16 | pass→pass | 15,101 | 11,667 | -23% | 1 | 1 | 0% | 2,261 | 2,689 | +19% | 0 | 0 | — |
case-17 | pass→pass | 7,950 | 4,405 | -45% | 1 | 1 | 0% | 1,159 | 2,147 | +85% | 0 | 0 | — |
case-18 | pass→pass | 11,458 | 5,752 | -50% | 1 | 1 | 0% | 2,034 | 2,415 | +19% | 0 | 0 | — |
case-19 | pass→pass | 11,273 | 11,491 | +2% | 1 | 1 | 0% | 1,993 | 2,776 | +39% | 0 | 0 | — |
case-20 | fail→pass | 8,927 | 6,282 | -30% | 1 | 1 | 0% | 1,501 | 2,027 | +35% | 0 | 0 | — |
case-21 | fail→pass | 14,814 | 5,696 | -62% | 1 | 1 | 0% | 2,672 | 2,314 | -13% | 0 | 0 | — |
case-22 | pass→pass | 10,109 | 6,262 | -38% | 1 | 1 | 0% | 1,809 | 2,560 | +42% | 0 | 0 | — |
case-23 | pass→pass | 16,163 | 7,892 | -51% | 1 | 1 | 0% | 1,732 | 3,013 | +74% | 0 | 0 | — |
case-24 | pass→fail | 11,533 | 9,264 | -20% | 1 | 1 | 0% | 2,223 | 3,109 | +40% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 24 cases were attempted. The headline lift of +46 percentage points is the difference between those two pass rates over the 24 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.