Plugin install path resolution: outdated project-scope entry shadows user-scope install (still reproducible on 2.1.121)
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
- Install plugin from project A (project scope), e.g. version 1.0.0
- Later, install the same plugin at user scope, e.g. version 1.10.0
installed_plugins.jsonends up with the project-scope (1.0.0) entry first, then user-scope (1.10.0)- Open Claude Code in unrelated project B
- 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
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗