Duplicate plugins loaded when same plugin exists in multiple sources #4488

Closed
opened 2026-02-16 17:44:20 -05:00 by yindo · 1 comment
Owner

Originally created by @devxoul on GitHub (Jan 9, 2026).

Originally assigned to: @thdxr on GitHub.

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.jsfoo
  • npm packages: Extract package name without version
    • oh-my-opencode@2.4.3oh-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:

  1. Reverse the plugin list
  2. Deduplicate by canonical name (keeping first occurrence = highest priority)
  3. 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

  • getPluginName correctly extracts canonical names from:
    • file:// URLs (with .js and .ts extensions)
    • npm packages with versions
    • Scoped packages with versions
    • Plain package names without versions
  • deduplicatePlugins correctly:
    • Removes duplicates by canonical name
    • Preserves highest priority plugin
    • Maintains relative order of unique plugins
  • Integration test confirms local plugin overrides global config
  • No breaking changes for users without duplicate plugins

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
Originally created by @devxoul on GitHub (Jan 9, 2026). Originally assigned to: @thdxr on GitHub. ## 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: 1. Reverse the plugin list 2. Deduplicate by canonical name (keeping first occurrence = highest priority) 3. 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 - [ ] `getPluginName` correctly extracts canonical names from: - file:// URLs (with .js and .ts extensions) - npm packages with versions - Scoped packages with versions - Plain package names without versions - [ ] `deduplicatePlugins` correctly: - Removes duplicates by canonical name - Preserves highest priority plugin - Maintains relative order of unique plugins - [ ] Integration test confirms local plugin overrides global config - [ ] No breaking changes for users without duplicate plugins ## 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
yindo closed this issue 2026-02-16 17:44:20 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Jan 9, 2026):

This issue might be a duplicate of existing issues. Please check:

  • #3179: plugins are loaded twice
  • #6596: Plugin Registering Twice
  • #6261: multiple plugins registering auth for the same provider silently overwrite each other
  • #7163: Security Issue - Autoloading Repository Plugins

Feel free to ignore if none of these address your specific case.

@github-actions[bot] commented on GitHub (Jan 9, 2026): This issue might be a duplicate of existing issues. Please check: - #3179: plugins are loaded twice - #6596: Plugin Registering Twice - #6261: multiple plugins registering auth for the same provider silently overwrite each other - #7163: Security Issue - Autoloading Repository Plugins Feel free to ignore if none of these address your specific case.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#4488