Architecture
Plugin System
How opencode plugins work and how to extend Org in a Box with your own.
Overview
Org in a Box extends opencode via the @opencode-ai/plugin SDK. Each plugin is a TypeScript module that exports a Plugin function — opencode calls it on startup and registers the returned hooks.
Bundled Plugins
| Plugin | Package | Key Hooks / Tools |
|---|---|---|
plugin-memory | packages/plugins/memory | chat.message, system.transform, memory tool |
plugin-permissions | packages/plugins/permissions | permission.asked → auto-approve/deny |
plugin-analytics | packages/plugins/analytics | tool.execute.after → usage_events |
plugin-session-search | packages/plugins/session-search | session_search tool |
plugin-subagent | packages/plugins/subagent | subagent tool (creates child sessions) |
plugin-tunnel-tools | packages/plugins/tunnel-tools | tunnel_connect, tunnel_disconnect |
plugin-host-tools | packages/plugins/host-tools | python_exec, email/calendar stubs |
plugin-orchestrator | packages/plugins/orchestrator | plan_tasks, plan_status tools |
plugin-reflection | packages/plugins/reflection | session.status → trajectory + skill proposal |
plugin-browser | packages/plugins/browser | browser_navigate, browser_click, etc. |
plugin-skill-autowrite | packages/plugins/skill-autowrite | Auto-writes promoted skills to SKILL.md |
plugin-claude-oauth | packages/plugins/claude-oauth | Claude.ai OAuth credential management |
Plugin Structure
import type { Plugin, Hooks } from "@opencode-ai/plugin"
export const plugin: Plugin = async (input, options) => {
// initialization (DB connections, config, etc.)
const hooks: Hooks = {
// Hook handlers
"chat.message": async (ctx, message) => { /* … */ },
"experimental.chat.system.transform": async (ctx, out) => {
out.system.push("Your custom system prompt content")
},
// Tool definitions
tool: {
my_tool: tool({
description: "…",
args: { query: z.string() },
async execute(args, ctx) {
return `result: ${args.query}`
},
}),
},
}
return hooks
}
export default plugin
Available Hooks
| Hook | When It Fires | Input | Output |
|---|---|---|---|
chat.message | Every message | ctx, message | void |
tool.execute.after | After every tool call | { tool, input, output, sessionID } | void |
session.status | Session status changes | { sessionID, status } | void |
experimental.chat.system.transform | Before every turn | { sessionID, model } | { system: string[] } |
experimental.session.compacting | Before compaction | { sessionID } | { context: string[], prompt?: string } |
experimental.compaction.autocontinue | During compaction | { sessionID } | { enabled: boolean } |
permission.asked | Permission request | { sessionID, tool, permission } | void |
Registering Plugins
Edit config/opencode.jsonc:
{
"plugin": [
"@orginabox/plugin-memory",
"@orginabox/plugin-orchestrator",
"./path/to/my-custom-plugin"
]
}
Writing a Custom Plugin
- Create a new package in
packages/plugins/my-plugin/ - Add
package.jsonwith"main": "src/index.ts"and dep on@opencode-ai/plugin - Export a
plugin: Pluginfunction - Add the package to the root workspace in
package.json - Add the package name to
config/opencode.jsonc
