スキル一覧に戻る
ian-pascoe

writing-plugins

by ian-pascoe

My dotfiles

1🍴 0📅 2026年1月24日
GitHubで見るManusで実行

SKILL.md


name: writing-plugins description: Use when creating, editing, or reviewing OpenCode plugins. Covers plugin structure, event hooks, TypeScript typing, custom tools, dependency management, and common patterns like notifications and compaction hooks.

Writing Plugins

Overview

Plugins extend OpenCode by hooking into events and customizing behavior. They're JavaScript/TypeScript modules that export functions receiving a context object and returning hooks.

When to Use

  • Creating event-driven automation (notifications, logging, protection)
  • Adding custom tools to OpenCode
  • Modifying tool behavior before/after execution
  • Customizing session compaction behavior
  • Integrating with external services

Don't use for: simple prompts (use commands), static configuration (use config), or agent behavior (use agents).

File Locations

.opencode/plugin/<name>.js|ts       # Project-local
~/.config/opencode/plugin/<name>.js|ts  # Global/personal

Or via npm in opencode.json:

{
  "plugin": ["opencode-helicone-session", "@my-org/custom-plugin"]
}

Load Order

  1. Global config (~/.config/opencode/opencode.json)
  2. Project config (opencode.json)
  3. Global plugin directory (~/.config/opencode/plugin/)
  4. Project plugin directory (.opencode/plugin/)

Basic Structure

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

export const MyPlugin: Plugin = async ({
  project,
  client,
  $,
  directory,
  worktree,
}) => {
  // Initialization code here

  return {
    // Hook implementations
  };
};

Context Object

PropertyDescription
projectCurrent project information
directoryCurrent working directory
worktreeGit worktree path
clientOpenCode SDK client for AI interaction
$Bun's shell API for executing commands

Event Hooks

Event Categories

CategoryEvents
Sessionsession.created, session.idle, session.compacted, session.deleted, session.error, session.status, session.updated, session.diff
Tooltool.execute.before, tool.execute.after
Messagemessage.updated, message.removed, message.part.updated, message.part.removed
Filefile.edited, file.watcher.updated
Permissionpermission.updated, permission.replied
TUItui.prompt.append, tui.command.execute, tui.toast.show
LSPlsp.client.diagnostics, lsp.updated
Othercommand.executed, installation.updated, server.connected, todo.updated

Hook Patterns

// Event subscription
return {
  event: async ({ event }) => {
    if (event.type === "session.idle") {
      // Handle event
    }
  },
};

// Tool interception
return {
  "tool.execute.before": async (input, output) => {
    // Modify or block tool execution
  },
  "tool.execute.after": async (input, output) => {
    // React to tool completion
  },
};

Custom Tools

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

export const CustomToolsPlugin: Plugin = async (ctx) => {
  return {
    tool: {
      mytool: tool({
        description: "This is a custom tool",
        args: {
          foo: tool.schema.string(),
        },
        async execute(args, ctx) {
          return `Hello ${args.foo}!`;
        },
      }),
    },
  };
};

Dependencies

Add external packages via .opencode/package.json:

{
  "dependencies": {
    "shescape": "^2.1.0"
  }
}

OpenCode runs bun install at startup. npm plugins are cached in ~/.cache/opencode/node_modules/.

Quick Reference

PatternExample Use Case
event handlerNotifications on session.idle
tool.execute.beforeBlock .env reads, sanitize commands
tool.execute.afterLog tool usage, track metrics
tool definitionAdd domain-specific tools
experimental.session.compactingCustom compaction context

Common Patterns

Notification on Completion

return {
  event: async ({ event }) => {
    if (event.type === "session.idle") {
      await $`osascript -e 'display notification "Done!" with title "opencode"'`;
    }
  },
};

Tool Protection

return {
  "tool.execute.before": async (input, output) => {
    if (input.tool === "read" && output.args.filePath.includes(".env")) {
      throw new Error("Do not read .env files");
    }
  },
};

Structured Logging

await client.app.log({
  service: "my-plugin",
  level: "info", // debug, info, warn, error
  message: "Plugin initialized",
  extra: { foo: "bar" },
});

Compaction Context

return {
  "experimental.session.compacting": async (input, output) => {
    // Add context (appended to default prompt)
    output.context.push(`## Custom Context\n...`);

    // OR replace entire prompt
    output.prompt = `Your custom compaction prompt...`;
  },
};

Common Mistakes

MistakeFix
Using console.logUse client.app.log() for structured logging
Missing TypeScript typesImport Plugin from @opencode-ai/plugin
Throwing in hooks without intentThrow to block, return to allow
Forgetting async on handlersAll hooks should be async functions
Local deps without package.jsonAdd .opencode/package.json for npm packages

Validation Checklist

  • Plugin exports named function (not default)
  • Function is async and returns hooks object
  • TypeScript uses Plugin type import
  • Event types checked before handling
  • External deps listed in package.json
  • Uses client.app.log() not console.log
  • Tool definitions have description and args schema

スコア

総合スコア

50/100

リポジトリの品質指標に基づく評価

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

レビュー

💬

レビュー機能は近日公開予定です