Problem
When the same plugin exists in both opencode.json configuration and the .opencode/plugin/ directory, both instances are loaded and executed, causing duplicate behavior and potential conflicts.
Example Scenario
A user has:
oh-my-opencode@2.4.3 specified in their opencode.json
oh-my-opencode.js in their local .opencode/plugin/ directory (perhaps a development version)
Current behavior: Both plugins are loaded and executed, leading to:
- Duplicate side effects
- Potential version conflicts
- Confusing behavior when testing local modifications
- Resource waste from running the same plugin twice
Expected behavior: Only the highest priority plugin should be loaded, with a clear priority hierarchy.
Proposed Solution
Implement plugin deduplication by canonical name with priority-based resolution:
1. Plugin Name Extraction (getPluginName)
Extract a canonical name from various plugin specifier formats:
- file:// URLs: Extract filename without extension
file:///path/to/plugin/foo.js → foo
- npm packages: Extract package name without version
oh-my-opencode@2.4.3 → oh-my-opencode
- Scoped packages: Preserve scope, remove version
@scope/pkg@1.0.0 → @scope/pkg
2. Priority-Based Deduplication (deduplicatePlugins)
When multiple plugins with the same canonical name are found, use this priority order (highest to lowest):
| Priority |
Source |
Rationale |
| 1 (Highest) |
Local plugin/ directory |
Development/override takes precedence |
| 2 |
Local opencode.json |
Project-specific config |
| 3 |
Global plugin/ directory |
User-wide overrides |
| 4 (Lowest) |
Global opencode.json |
User-wide defaults |
3. Implementation Approach
Since plugins are collected in low-to-high priority order:
- Reverse the plugin list
- Deduplicate by canonical name (keeping first occurrence = highest priority)
- Restore original order
Use Cases
Use Case 1: Local Development Override
Global opencode.json: "oh-my-opencode@2.4.3"
Local plugin/: "oh-my-opencode.js" (modified version)
→ Only local plugin/oh-my-opencode.js is loaded
Use Case 2: Project-Specific Version
Global opencode.json: "@company/plugin@1.0.0"
Local opencode.json: "@company/plugin@2.0.0"
→ Only @company/plugin@2.0.0 from local config is loaded
Use Case 3: No Duplicates
Local opencode.json: "plugin-a", "plugin-b"
→ Both loaded normally (no deduplication needed)
Acceptance Criteria
Additional Context
- This is a configuration enhancement in
packages/opencode/src/config/config.ts
- The fix should be transparent to users who don't have duplicate plugins
- Users with intentional duplicates (e.g., different scoped packages with same base name but different functionality) should not be affected since the canonical name includes the scope
Problem
When the same plugin exists in both
opencode.jsonconfiguration and the.opencode/plugin/directory, both instances are loaded and executed, causing duplicate behavior and potential conflicts.Example Scenario
A user has:
oh-my-opencode@2.4.3specified in theiropencode.jsonoh-my-opencode.jsin their local.opencode/plugin/directory (perhaps a development version)Current behavior: Both plugins are loaded and executed, leading to:
Expected behavior: Only the highest priority plugin should be loaded, with a clear priority hierarchy.
Proposed Solution
Implement plugin deduplication by canonical name with priority-based resolution:
1. Plugin Name Extraction (
getPluginName)Extract a canonical name from various plugin specifier formats:
file:///path/to/plugin/foo.js→foooh-my-opencode@2.4.3→oh-my-opencode@scope/pkg@1.0.0→@scope/pkg2. Priority-Based Deduplication (
deduplicatePlugins)When multiple plugins with the same canonical name are found, use this priority order (highest to lowest):
plugin/directoryopencode.jsonplugin/directoryopencode.json3. Implementation Approach
Since plugins are collected in low-to-high priority order:
Use Cases
Use Case 1: Local Development Override
Use Case 2: Project-Specific Version
Use Case 3: No Duplicates
Acceptance Criteria
getPluginNamecorrectly extracts canonical names from:deduplicatePluginscorrectly:Additional Context
packages/opencode/src/config/config.ts