Plugin UI shows local-scoped plugin as 'installed' in unrelated projects, blocks reinstall with different scope

Resolved 💬 3 comments Opened Feb 18, 2026 by ealexisaraujo Closed Feb 21, 2026

Bug Report

Version: Claude Code 2.1.45 (latest as of 2026-02-17)
OS: macOS 26.3 (Darwin 25.3.0)

---

Summary

When a plugin is installed with scope: "local" for Project A, the /plugin UI shows it as "installed" (green checkmark) when browsing from Project B (an unrelated project). This is misleading because the plugin does not actually load in Project B. Worse, the "installed" state prevents the user from reinstalling it with a different scope (e.g., user), effectively locking them out.

Additional issue: Even removing the marketplace entirely and re-adding it does NOT fix the problem. The installed_plugins.json record persists independently of marketplace state, so the plugin continues to show as "installed" with no UI path to reinstall it. The only workaround is manually editing the JSON file.

---

Steps to Reproduce

  1. Open Claude Code in Project A (/path/to/project-a/)
  2. Run /plugin → browse a marketplace → install a plugin (e.g., compound-engineering@every-marketplace)
  3. When prompted for scope, choose "Install for you, in this repo only (local scope)"
  4. The plugin installs successfully. Verify:
  • installed_plugins.json gets a record with "scope": "local" and "projectPath": "/path/to/project-a/"
  • project-a/.claude/settings.local.json gets "enabledPlugins": { "compound-engineering@every-marketplace": true }
  1. Close Claude Code
  2. Open Claude Code in Project B (/path/to/project-b/) — a completely unrelated project
  3. Run /plugin → browse the same marketplace
  4. Observe: the plugin shows ✔ compound-engineering (installed) with a green checkmark
  5. Try to use a command from the plugin (e.g., /workflows:brainstorm) → fails with "Unknown skill"
  6. Try to install the plugin again for Project B or as user-scope → cannot, because the UI treats it as already installed
  7. Try removing the marketplace and re-adding it → the plugin still shows as "installed" because the record lives in installed_plugins.json, not in the marketplace data. There is no UI path to clear this state.

---

Expected Behavior

  • In Project B, the plugin should show as not installed (radio button, not checkmark), since it is only locally scoped to Project A
  • Alternatively, if showing it as "installed", the UI should indicate the scope (e.g., "installed in /path/to/project-a/ only") and offer an option to install it for the current project or globally
  • The user should be able to install the same plugin with a different scope without manual JSON editing
  • Removing and re-adding a marketplace should allow reinstallation, or at minimum the UI should provide a "reinstall" or "change scope" option

---

Actual Behavior

  • The plugin shows as "installed" in all projects, regardless of scope
  • The plugin does not load (commands/skills are unavailable) because enabledPlugins is only in Project A's settings.local.json
  • There is no UI path to reinstall with a different scope — the user is stuck
  • Removing the marketplace and re-adding it does NOT reset the "installed" state — the user is completely locked out without manual JSON editing

---

Root Cause Analysis

The issue is in the r0() / EH9() functions that determine whether a plugin is installed:

// r0(pluginId) — "is installed?"
function r0(A) { return EH9(A) !== void 0 }

// EH9(pluginId) — get install info
function EH9(A) {
  let K = n0().plugins[A];       // reads from installed_plugins.json
  if (!K || K.length === 0) return;
  let Y = K[0];                  // ← takes the FIRST record, ignores scope/projectPath
  if (!Y) return;
  return { version: Y.version, installedAt: Y.installedAt, ... }
}

EH9() returns the first record from the installed_plugins.json array for that plugin — it does not filter by scope or compare projectPath against the current working directory (CA()).

Meanwhile, the scope-aware function nZ6(pluginId) does exist and correctly matches against CA():

function nZ6(A) {
  let K = n0().plugins[A];
  let Y = CA();  // current project path
  let z = K.find(H => H.scope === "local" && H.projectPath === Y);
  if (z) return { scope: z.scope, projectPath: z.projectPath };
  let w = K.find(H => H.scope === "project" && H.projectPath === Y);
  if (w) return { scope: w.scope, projectPath: w.projectPath };
  let $ = K.find(H => H.scope === "user");
  if ($) return { scope: $.scope };
  return { scope: K[0].scope, projectPath: K[0].projectPath };
}

But nZ6() is not used by the UI's "is installed?" check — only r0() / EH9() are.

Why removing the marketplace doesn't help: The marketplace removal only clears marketplace metadata (known_marketplaces.json and the marketplaces/ directory). The installed_plugins.json records are independent and persist. Since r0() reads from installed_plugins.json, the plugin continues to appear as "installed" even after the marketplace is removed and re-added.

---

Suggested Fix

r0(pluginId) should be scope-aware. A plugin should only be considered "installed" for the current context if:

  1. It has a record with scope: "user" (global — always counts), OR
  2. It has a record with scope: "project" or scope: "local" where projectPath === CA() (matches current directory)

Something like:

function r0(A) {
  let K = n0().plugins[A];
  if (!K || K.length === 0) return false;
  let Y = CA();
  return K.some(H =>
    H.scope === "user" ||
    H.scope === "managed" ||
    ((H.scope === "project" || H.scope === "local") && H.projectPath === Y)
  );
}

Additionally, the plugin details UI (QZ6) could show scope-specific install options when the plugin is installed elsewhere but not in the current project.

---

Workaround

Manually edit ~/.claude/plugins/installed_plugins.json:

  1. Find the plugin entry (e.g., "compound-engineering@every-marketplace")
  2. Change "scope": "local" to "scope": "user"
  3. Remove the "projectPath" field
  4. Restart Claude Code

Or add "compound-engineering@every-marketplace": true to ~/.claude/settings.json under enabledPlugins to enable it globally.

Note: There is no way to fix this through the UI alone — removing the marketplace and re-adding it does not help.

---

Related State

installed_plugins.json record (problematic):

"compound-engineering@every-marketplace": [
  {
    "scope": "local",
    "projectPath": "/path/to/project-a",
    "installPath": "~/.claude/plugins/cache/every-marketplace/compound-engineering/2.27.0",
    "version": "2.27.0",
    "installedAt": "2026-01-21T21:12:46.796Z"
  }
]

Project A's .claude/settings.local.json (correct — has enabledPlugins):

{
  "enabledPlugins": {
    "compound-engineering@every-marketplace": true
  }
}

Project B's .claude/settings.local.json (missing — no enabledPlugins for this plugin):
The plugin is not listed here, so it never loads, but the UI still shows it as "installed".

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗