Plugin install path resolution: outdated project-scope entry shadows user-scope install (still reproducible on 2.1.121)

Resolved 💬 1 comment Opened Apr 28, 2026 by morganBlanloeil Closed May 29, 2026

Summary

When a plugin has multiple entries in ~/.claude/plugins/installed_plugins.json (e.g. one project-scope install for an old project + one user-scope install), Claude Code can resolve the plugin's install path using the first entry in the array (B[0]) without matching against the current working directory. A stale project-scope install can therefore silently shadow the up-to-date user-scope install across all projects.

User symptom: skills added in recent versions of a plugin disappear from the available skill list, even though the plugin's marketplace clone is fully up-to-date and the user-scope install records the latest version.

Repro

  1. Install plugin from project A (project scope), e.g. version 1.0.0
  2. Later, install the same plugin at user scope, e.g. version 1.10.0
  3. installed_plugins.json ends up with the project-scope (1.0.0) entry first, then user-scope (1.10.0)
  4. Open Claude Code in unrelated project B
  5. Run any skill from the plugin — only skills present in 1.0.0 are loaded; skills added between 1.0.0 → 1.10.0 are silently missing

Expected

When cwd does not match any project-scope projectPath in the plugin's entries, Claude Code should fall back to the user-scope (or managed) entry, not blindly use [0].

Evidence — affected versions

In bundled cli.js from versions 2.0.72 → 2.1.14:

function Ts4(A) {
  let B = aE().plugins[A];
  if (!B || B.length === 0) return;
  let G = B[0];                     // ← always takes first entry
  if (!G) return;
  return {
    version: G.version || "unknown",
    installedAt: G.installedAt || new Date().toISOString(),
    lastUpdated: G.lastUpdated,
    installPath: G.installPath,
    gitCommitSha: G.gitCommitSha
  }
}

In 2.1.72 / 2.1.74, this is refactored to pE1, which correctly tries local→project (cwd-matched)→user→fallback:

function pE1(A) {
  let K = cW().plugins[A];
  if (!K || K.length === 0) return { scope: "user" };
  let Y = s8();  // cwd
  let z = K.find(O => O.scope === "local"   && O.projectPath === Y); if (z) return ...;
  let _ = K.find(O => O.scope === "project" && O.projectPath === Y); if (_) return ...;
  let w = K.find(O => O.scope === "user"); if (w) return ...;
  return { scope: K[0].scope, projectPath: K[0].projectPath };  // last-resort fallback
}

Still reproducible on 2.1.121

Despite the refactor in 2.1.72+, the symptom is still reproducible on 2.1.121 (current native binary, macOS arm64). After manually reordering installed_plugins.json so the user-scope entry is [0], all plugin skills load correctly. So either there is a regression, or there is another code path in the skill-loading pipeline that still uses [0]-indexed lookup. Couldn't dig further into the native binary.

Workaround

Manually reorder entries in ~/.claude/plugins/installed_plugins.json so the user-scope (or correct project-scope) entry is [0], then restart Claude Code.

Environment

  • Claude Code 2.1.121 (native binary, macOS arm64) — symptom reproduced
  • Confirmed buggy code in npm builds 2.0.72, 2.0.76, 2.1.7, 2.1.12, 2.1.14
  • Confirmed fixed code in npm builds 2.1.72, 2.1.74

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗