Skip to content

plugin: local directory plugin loads via package.json exports instead of index.js #59859

Open

Description

@seppaleinen

Summary

OpenCode v2.2.0 loads local plugins via Node.js ESM resolution (package.json exports) instead of directly loading the directory's index.js file as documented. This breaks plugins like hindsight-coding-agents that provide dual v1/v2 compatibility by exporting the v1 plugin from dist/index.js (via package.json main) and the v2 plugin from the root index.js.

Environment

  • opencode version: 2.2.0
  • OS: Darwin 27.0.0 (darwin arm64)
  • Terminal: TERM_PROGRAM=ghostty, TERM=xterm-256color, COLORTERM=truecolor
  • Shell: /bin/zsh
  • Install/channel: latest (release)
  • Active plugins: /Users/seppa/.hindsight/coding-agents, opencode-plugin-litellm@latest

Reproduction

  1. Install the hindsight-coding-agents plugin: npx @vectorize-io/hindsight-coding-agents install opencode2
  2. Verify opencode.json contains: "plugin": ["/Users/seppa/.hindsight/coding-agents"]
  3. Start opencode: opencode
  4. Observe plugin load failure in logs

Expected Behavior

Per the OpenCode v2 documentation: "Configured local paths must be directories. OpenCode loads the directory's server or index module, such as index.ts."

OpenCode should load /Users/seppa/.hindsight/coding-agents/index.js (the root index.js) which exports the v2 plugin with { id: 'hindsight-opencode2', setup: [AsyncFunction] }.

Actual Behavior

OpenCode loads /Users/seppa/.hindsight/coding-agents/dist/index.js (via Node.js ESM resolution using package.json exports field) which exports the v1 plugin factory function.

Error in server log:

failed to load plugin target=/Users/seppa/.hindsight/coding-agents ref=err_8fbc317d cause="Cause([Fail(PluginModule.LoadError: Plugin must export a default definition with an id and an effect or setup function. (cause: SchemaError(Expected object at ["default"])))])"

The v1 plugin factory function doesn't match the v2 plugin schema (which requires { id, setup } or { id, effect }).

Additional Context

The hindsight-coding-agents plugin (v0.8.2) is designed for dual v1/v2 compatibility:

  • v1/Node.js: resolves via package.json main → dist/index.js (v1 plugin)
  • v2: should resolve via <dir>/index.js → root index.js (v2 plugin re-exporting from dist/opencode2.js)

This worked in earlier OpenCode versions but broke in v2.2.0.

The plugin's package.json has:

{
  "main": "dist/index.js",
  "exports": { ".": { "default": "./dist/index.js" } }
}

OpenCode v2.2.0 appears to be using the exports field for resolution instead of directly loading the directory's index.js.

Related hindsight issues (already filed upstream):

Activity

  1. opencodejr commented on Oct 28, 2026

    @opencodejr

    Thanks for the detailed report!

    This comes from a deliberate change in 2.2.0 to how local plugin directories are resolved, so it isn't a loader bug. If a local directory's package.json has both name and exports, OpenCode now loads it through those fields, the same way as a published package: it tries the ./server export first, then .. The root server/index files are only used when those fields are missing. The Load section of the plugin docs describes this: "A local plugin directory with a package.json name and exports loads its parts through them, like the published package. Without them, it uses file names at its root."

    hindsight-coding-agents only exports . → ./dist/index.js (the v1 factory), so v2 ends up with that module and the schema check fails. The plugin can support both versions by adding a ./server export for its v2 entry:

    "exports": {
      ".": { "default": "./dist/index.js" },
      "./server": "./index.js"
    }

    Node and v1 keep resolving ., and OpenCode v2 picks up ./server. That's probably worth passing on to the upstream issues you linked.

    I've logged this as a feature request: either prefer a local directory's root index/server file over exports, or make this case easier to diagnose (for example, an error that names the file it resolved and suggests a ./server export). Maintainers will consider it.

    Related:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions