Skip to content

fix(plugins): marketplace hook supplement drops plugin.json matchers (missing mergeHooksSettings) #1055

@rodrigoeqnit

Description

@rodrigoeqnit

Bug Report

File: src/utils/plugins/pluginLoader.ts
Function: finishLoadingPluginFromPath
Lines: ~2913–2919 (current main)


Description

When a plugin has both a plugin.json (with hooks) and a marketplace entry that supplements hooks for the same event, the current code uses an object spread:

// Supplement hooks from marketplace entry
if (entry.hooks) {
  plugin.hooksConfig = {
    ...(plugin.hooksConfig || {}),
    ...(entry.hooks as HooksSettings),   // ← overwrites same-event arrays
  }
}

HooksSettings values are arrays of matchers keyed by event name (e.g. PreToolUse, PostToolUse). The spread replaces the entire event array from plugin.json with the marketplace entry's array, silently dropping any matchers already registered by the plugin manifest.


Root cause

mergeHooksSettings() already exists in the same file (line ~1861) and concatenates per-event arrays correctly:

merged[event] = [...(merged[event] || []), ...matchers]

It is used correctly in createPluginFromPath (lines ~1722, ~1759) but was not applied in the marketplace supplement path in finishLoadingPluginFromPath.


Reproduction (mental model)

  1. Plugin foo has plugin.json declaring PreToolUse: [matcherA]
  2. Marketplace entry for foo supplements with hooks: { PreToolUse: [matcherB] }
  3. Expected: hooksConfig.PreToolUse = [matcherA, matcherB]
  4. Actual: hooksConfig.PreToolUse = [matcherB] ← matcherA lost

Fix

Replace the spread with the existing mergeHooksSettings():

// Supplement hooks from marketplace entry
if (entry.hooks) {
  plugin.hooksConfig = mergeHooksSettings(
    plugin.hooksConfig,
    entry.hooks as HooksSettings,
  )
}

A PR with this fix is available: https://github.com/rodrigoeqnit/openclaude/compare/fix/pluginloader-hooks-merge


Severity

Medium — hooks from plugin.json are silently dropped in the marketplace supplement path. The regression is invisible (no error, no warning), making it hard to debug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions