Summary
Local plugins written with the documented V2 pattern — import { Plugin } from "@opencode/plugin" plus export default Plugin.define(...) — fail to load in OpenCode 2.0.12 with Cannot find package '@opencode/plugin'. Node and Bun resolve the same import from the same directories without issue, so the server's plugin module resolver is at fault.
Environment
- opencode version: 2.0.12 (channel=latest)
- OS: Linux (Ubuntu, kernel 7.0.0-31-generic, x86_64, glibc)
- Terminal: xterm-256color, truecolor
- Shell: /bin/bash
- Install/channel: Homebrew tap
anomalyco/tap/opencode-v2
- Active plugins:
vexp-guard (local; kept working via the no-import workaround below). Earlier local plugins (rtk.ts) and the V1 package plugin openslimedit@latest also failed to load with the same resolution error after porting to the documented V2 pattern.
Reproduction
- Create a project with
.opencode/plugins/probe.js:
import { Plugin } from "@opencode/plugin"
export default Plugin.define({
id: "probe",
setup() {},
})
- Run a headless session with server logs:
opencode run --print-logs "hi" (or just open the TUI and inspect ~/.local/share/opencode/log/opencode.log).
- Observe the load failure:
failed to load plugin target=<project>/.opencode/plugins/probe.js
cause="Cause([Die(ResolveMessage: Cannot find package '@opencode/plugin' imported from <project>/.opencode/plugins/probe.js)])"
Expected Behavior
Local plugins using the documented @opencode/plugin import load and appear in the plugin list with source: local and status: active (visible via opencode api get /api/plugin).
Actual Behavior
@opencode/plugin cannot be resolved by the server's plugin bundler, so every local plugin written with the documented pattern fails to load. Isolation findings:
- Node resolves the same import from the same file:
node -e "import('@opencode/plugin').then(() => console.log('ok'))" works.
- Bun resolves it too:
bun -e "import('@opencode/plugin').then(() => console.log('ok'))" works.
- Installing
@opencode/plugin@2.0.12 (and its @opencode/* dependencies) into node_modules — both as an ancestor of the plugin file and in the global config dir ~/.config/opencode/node_modules — does not help.
- Unscoped bare imports DO resolve from the plugin directory (
import { parse } from "yaml" from the config-dir node_modules loaded fine), so the loader itself is healthy.
- Scoped and subpath imports fail even when the target files exist:
@opencode/ai/schema, yaml/dist/yaml.mjs, and — inside @opencode/plugin's own dist — @opencode/schema/agent, effect/unstable/httpapi, and the peer @opencode/theme.
- Plugins with no bare imports load fine: a plain
export default { id, setup(ctx) } using only node: builtins is accepted by the loader and receives the full context (ctx.tool.hook("execute.before", ...) works).
This looks like a resolver that mishandles package exports/subpath resolution for local plugin files, separate from the LOADER (the loader's shape validation of the default export works).
Additional Context
- Frequency: reproducible consistently (100%).
- Also reproduced with the official npm build:
npm install -g @opencode/cli@2.0.12 → native binary @opencode/cli-linux-x64 has the same BuildID as the brew binary and fails identically, so this is a runtime defect, not an installer quirk.
- Recent change: migrated from OpenCode 1.x to 2.0.12. The V1 plugin API breakage is expected, but the documented V2 replacement pattern for local plugins does not work either.
- Workaround currently in use (works, but bypasses the type-checked
Plugin.define): omit the bare import and export a plain object —
export default {
id: "vexp-guard",
async setup(ctx) {
await ctx.tool.hook("execute.before", (event) => {
// ...
})
},
}
Summary
Local plugins written with the documented V2 pattern —
import { Plugin } from "@opencode/plugin"plusexport default Plugin.define(...)— fail to load in OpenCode 2.0.12 withCannot find package '@opencode/plugin'. Node and Bun resolve the same import from the same directories without issue, so the server's plugin module resolver is at fault.Environment
anomalyco/tap/opencode-v2vexp-guard(local; kept working via the no-import workaround below). Earlier local plugins (rtk.ts) and the V1 package pluginopenslimedit@latestalso failed to load with the same resolution error after porting to the documented V2 pattern.Reproduction
.opencode/plugins/probe.js:opencode run --print-logs "hi"(or just open the TUI and inspect~/.local/share/opencode/log/opencode.log).Expected Behavior
Local plugins using the documented
@opencode/pluginimport load and appear in the plugin list withsource: localandstatus: active(visible viaopencode api get /api/plugin).Actual Behavior
@opencode/plugincannot be resolved by the server's plugin bundler, so every local plugin written with the documented pattern fails to load. Isolation findings:node -e "import('@opencode/plugin').then(() => console.log('ok'))"works.bun -e "import('@opencode/plugin').then(() => console.log('ok'))"works.@opencode/plugin@2.0.12(and its@opencode/*dependencies) intonode_modules— both as an ancestor of the plugin file and in the global config dir~/.config/opencode/node_modules— does not help.import { parse } from "yaml"from the config-dirnode_modulesloaded fine), so the loader itself is healthy.@opencode/ai/schema,yaml/dist/yaml.mjs, and — inside@opencode/plugin's own dist —@opencode/schema/agent,effect/unstable/httpapi, and the peer@opencode/theme.export default { id, setup(ctx) }using onlynode:builtins is accepted by the loader and receives the full context (ctx.tool.hook("execute.before", ...)works).This looks like a resolver that mishandles package exports/subpath resolution for local plugin files, separate from the LOADER (the loader's shape validation of the default export works).
Additional Context
npm install -g @opencode/cli@2.0.12→ native binary@opencode/cli-linux-x64has the same BuildID as the brew binary and fails identically, so this is a runtime defect, not an installer quirk.Plugin.define): omit the bare import and export a plain object —