AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Opencode Build Plugins

skill-pantheon-org-tekhne-build-plugins · by pantheon-org

Create OpenCode plugins using the opencode-ai/plugin SDK. Use when user wants to build a plugin, extend OpenCode, intercept tool execution, add custom tools, react to events, create a hook, block commands, add custom auth, or add automation to OpenCode. Also use for 'opencode plugin', 'custom tool', 'tool hook', 'plugin hook', 'logging for all tool calls', 'restrict what commands the AI can run',…

No reviews yet
0 installs
40 views
0.0% view→install

Install

$ agentstack add skill-pantheon-org-tekhne-build-plugins

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-pantheon-org-tekhne-build-plugins)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Opencode Build Plugins? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Creating OpenCode Plugins

Quick Start

Plugins live in .opencode/plugins//index.ts (project) or ~/.config/opencode/plugins//index.ts (global).

import type { Plugin } from "@opencode-ai/plugin"

export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
  return {
    // Add hooks here — see references/hooks.md for available hooks
  }
}

Register in opencode.json:

{ "plugin": ["file:///.opencode/plugins/my-plugin/index.ts"] }

Optional — document plugin purpose in a companion agent file:

---
description: Plugin guard — intercepts tool calls, validates bash commands, blocks dangerous deletions, logs audit trail, hooks session events
---

Then test: opencode run hi

Mindset

Plugins are async factory functions returning hook implementations. They run once on load. Hooks fire at lifecycle points (tool execution, events, config load). Think of them as middleware layers.

Use tool.execute.before to intercept/block, tool.execute.after to log, event: for file edits/session end, auth: for custom model auth, chat.params: to modify LLM params, tool: key to add callable tools.

When to use: Intercept/log tool calls, add custom auth, react to lifecycle events, or extend OpenCode with new callable tools.

When NOT to use: Built-in tool exists (check client.tool.list()). Only need a shortcut (use slash command). Need behavior guidance (use AGENTS.md). Need MCP (use Model Context Protocol).

Verify plugin is loaded: run bun run opencode run "list your tools" and confirm the plugin output appears.

Anti-Patterns

NEVER call client.registerTool() — it does not exist. WHY: The SDK has no such method; calling it throws at runtime.

// BAD - runtime error
client.registerTool("my-tool", { ... })

// GOOD - return the tool from the factory
export const Plugin: Plugin = async ({ client }) => ({
  tool: { "my-tool": myTool }
})

NEVER write sync hook handlers. WHY: The plugin loader validates async signatures; a sync handler causes the entire plugin to be rejected at load time with a type error.

// BAD - rejected at load
"tool.execute.before": (input) => { log(input) }

// GOOD - always async
"tool.execute.before": async (input) => { log(input) }

NEVER mutate input.args in tool.execute.before to block a tool — the input is read-only and silently ignored. WHY: Mutations have no effect; to block execution you must throw.

// BAD - silently ignored
"tool.execute.before": async (input) => { input.args.command = "echo safe" }

// GOOD - throw to block
"tool.execute.before": async (input) => {
  if (isDangerous(input.args)) throw new Error("Blocked")
}

See [references/hook-patterns.md](references/hook-patterns.md) for complete anti-pattern list + full hook/event/tool/UI/testing/publishing reference.

Eval Scenarios

  • [Scenario 0: Block dangerous bash commands with tool.execute.before](evals/scenario-0/task.md)
  • [Scenario 1: Show toast notifications on file edit events](evals/scenario-1/task.md)
  • [Scenario 2: Add custom tool to plugin using tool key](evals/scenario-2/task.md)

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.