Skip to content

Plugin deduplication drops package plugins when import.meta.resolve rewrites them to dist/index.js #18518

@minzique

Description

@minzique

Summary

OpenCode resolves plugin entries with import.meta.resolve() before calling deduplicatePlugins(). When a package resolves to a file URL ending in dist/index.js, getPluginName() reduces it to basename index, so multiple unrelated plugins collide and only the last one survives.

Repro

Given config like:

{
  "plugin": [
    "opencode-claude-auth",
    "oh-my-opencode",
    "oc-codex-multi-account@latest",
    "opencode-memory-plugin"
  ]
}

and locally installed packages that resolve to:

  • file:///.../node_modules/opencode-claude-auth/dist/index.js
  • file:///.../node_modules/oc-codex-multi-account/dist/index.js
  • file:///.../memory-plugin/index.ts

Config.loadFile() resolves those package names first, then deduplicatePlugins() sees all three file URLs as the canonical plugin name index.

Result: only the last index plugin is kept, and the others are silently dropped.

Relevant code

  • packages/opencode/src/config/config.ts:1316 resolves plugin specifiers via import.meta.resolve()
  • packages/opencode/src/config/config.ts:481 canonicalizes file URLs using path.parse(...).name
  • packages/opencode/src/config/config.ts:503 deduplicates based on that canonical name

Why this is device-dependent

It only breaks on machines where the package is already locally resolvable during config parsing (for example via ~/.config/opencode/node_modules). On machines where the bare package name stays unresolved until plugin load time, dedupe uses the package name and everything works.

Expected

Unrelated plugins should not collide just because their resolved entrypoints are named index.js.

Actual

Different plugins silently collapse to the same canonical name index.

Workaround

Point the plugin entry at a uniquely named file URL instead of the bare package name, e.g.:

{
  "plugin": [
    "file:///.../node_modules/opencode-claude-auth/claude-auth-plugin.js"
  ]
}

Proposed fixes

Either of these would solve it in core:

  1. Deduplicate before import.meta.resolve() so package names remain package names.
  2. For resolved file URLs, derive the canonical plugin name from the nearest package.json name instead of the basename.
  3. At minimum, preserve the original plugin specifier for dedupe instead of using only the resolved file basename.

Metadata

Metadata

Assignees

Labels

coreAnything pertaining to core functionality of the application (opencode server stuff)

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions