Load plugins
Plugins can be loaded from npm packages, explicit local paths, or config directories. Each module must have one default export containing a unique pluginid and either a Promise setup function or an Effect effect
function.
Configuration
Add ordered entries to theplugins field in opencode.json(c):
opencode.jsonc
./ or ../ and resolve relative to the configuration file containing
the entry. Absolute paths and file:// URLs are also supported. Both scoped
packages and versioned package specifiers are supported.
Use the object form to pass JSON configuration to the plugin. OpenCode passes
options unchanged as ctx.options; omitted options become an empty object.
The plugin owns validation and defaults for its options.
See Config for configuration locations and precedence.
Entries from all applicable files are processed from lowest to highest
precedence rather than replacing the entire array.
Local discovery
OpenCode automatically scans this directory in every discovered OpenCode config directory:~/.config/opencode/plugins/. Direct .ts
and .js children are loaded. An immediate child directory is also loaded as a
package when OpenCode can resolve a string exports, module, or main
entrypoint, or an index.ts or index.js file.
A plugins/ directory beside a project-root opencode.json is not discovered
automatically. Put it under .opencode/, or add its file explicitly with a
relative config entry.
Enable and disable
A string beginning with- removes a previously selected target. * matches
everything, and a suffix of .* matches an ID or target prefix. Directives are
applied in order:
opencode.jsonc
Installation and dependencies
OpenCode installs bare package entries and their production dependencies into an isolated cache. Package installation does not run lifecycle scripts. Published packages should expose their plugin entrypoint and include every runtime import independencies.
Local files and local package directories are imported directly. OpenCode does
not install their dependencies. Install dependencies in a package.json
visible from the plugin file, for example:
effect is required for Effect plugins and for the Schema values used by
typed tools. A Promise plugin that does not define tools may only need
@opencode-ai/plugin. Match these versions to the OpenCode release you target.
Configuration and discovered plugin files under watched config directories are
reloaded when they change. Reloading replaces the active plugin generation and
releases its scoped registrations. Restart OpenCode after changing an npm
package version or a local dependency when no watched file changed.
Create a plugin
The Promise API is the simplest option. Export the result ofPlugin.define
as the module default:
.opencode/plugins/reviewer.ts
setup runs each time the plugin is activated for a Location. Register
long-lived behavior during setup; do not wait there on an infinite event
stream.
Effect plugins
Use the Effect entrypoint when the implementation benefits from Effect composition, fibers, or scoped resources:.opencode/plugins/reviewer-effect.ts
ctx capabilities.
Context
Promise methods return Promises; the equivalent Effect methods returnEffect. Read and action methods use the same inputs and location-aware
responses as the V2 client APIs.
| Capability | Available operations |
|---|---|
ctx.agent | list, transform, reload |
ctx.catalog.provider | list, get |
ctx.catalog.model | list, default |
ctx.catalog | transform, reload |
ctx.command | list, transform, reload |
ctx.integration | list, get, connect, attempt, transform, reload, and connection lookup/resolution |
ctx.plugin | list currently active plugin IDs |
ctx.reference | list, transform, reload |
ctx.session | create, get, prompt, command, interrupt, and hook |
ctx.skill | list, transform, reload |
ctx.tool | transform and hook |
ctx.aisdk | hook |
ctx.event | subscribe to the current public server event stream |
ctx.options | Readonly options from the matching config object |
$, directory, worktree, or a
general SDK client on the context. A plugin is Location-scoped, and the exposed
domain clients apply that Location by default.
Transform hooks
Transforms synchronously edit a draft whenever a stateful domain is built. Registering or disposing a transform rebuilds the domain from fresh state and runs all active transforms in order. Call the domain’sreload() method when
external data captured by a transform changes.
| Transform | Draft operations |
|---|---|
agent.transform | list, get, default, update, remove |
catalog.transform | Provider list, get, update, remove; model get, update, remove; default model get, set |
command.transform | list, get, update, remove |
integration.transform | Integration list, get, update, remove; method list, update, remove |
reference.transform | add, remove, list |
skill.transform | source, list |
tool.transform | add |
Registration with dispose for explicit cleanup. Tool
contributions currently remain until the owning plugin scope closes, so prefer
scope cleanup for plugin-wide teardown while this API is beta.
Runtime hooks
Runtime hooks intercept live operations. Their event objects expose specific mutable fields:| Hook | Mutable fields |
|---|---|
ctx.aisdk.hook("sdk", callback) | sdk, after inspecting model, package, and options |
ctx.aisdk.hook("language", callback) | language, after inspecting model, sdk, and options |
ctx.session.hook("request", callback) | system, messages, and the tools record immediately before model dispatch |
ctx.tool.hook("execute.before", callback) | input, before the selected tool executes |
ctx.tool.hook("execute.after", callback) | result, output, and outputPaths, after execution settles |
.opencode/plugins/guards.ts
Add a tool
UseTool.make with Effect schemas. Promise tools use async executors:
.opencode/plugins/greeting.ts
tools.add also accepts
{ group, deferred }:
groupprefixes and groups the exposed tool name.deferred: truemakes the tool available through the deferredexecutetool instead of exposing it directly.
sessionID,
agent, assistantMessageID, and toolCallID. Use
Tool.withPermission(tool, "permission-name") to assign a permission key.
Effect plugins import the helper from @opencode-ai/plugin/v2/effect/tool and
return an Effect from execute.
Types
Plugin.define infers the context and callbacks. The Promise root also
re-exports the canonical Agent, Command, Connection, Credential,
Integration, Model, Provider, Reference, and Skill schema namespaces.
Import narrower API types from their public subpaths when needed:
@opencode-ai/plugin/v2/effect, such as
@opencode-ai/plugin/v2/effect/plugin and
@opencode-ai/plugin/v2/effect/tool. Avoid importing types or runtime values
from @opencode-ai/core or @opencode-ai/server; those are private host
implementation details.
Publish a package
A package plugin uses the same default export as a local plugin. A minimal manifest is:package.json