Project-scope plugin installs: loader picks wrong array entry, no auto-update, broken UI removal

Resolved 💬 3 comments Opened May 1, 2026 by mikey-jay Closed May 1, 2026

Hit three related bugs around project-scope plugin installs in Claude Code 2.1.126. They compound — together they make project-scope installs unsuitable for any actively-iterated plugin.

Setup

A marketplace mymarketplace (with autoUpdate: true in known_marketplaces.json) hosts a plugin myplugin. The plugin has been actively iterated — its skills directory has grown over time. myplugin is installed at project scope in two different projects:

  • Project A (/path/to/project-a) — install record at older commit, fewer skills
  • Project B (/path/to/project-b) — install record at newer commit, all skills

The relevant slice of ~/.claude/plugins/installed_plugins.json:

{
  "plugins": {
    "myplugin@mymarketplace": [
      { "scope": "project", "projectPath": "/path/to/project-a", "installPath": ".../cache/.../<old-commit>", "version": "<old-commit>" },
      { "scope": "project", "projectPath": "/path/to/project-b", "installPath": ".../cache/.../<new-commit>", "version": "<new-commit>" }
    ]
  }
}

Both cache directories exist on disk and contain the SKILL.md files for their respective commits.

Bug 1: Loader picks first array entry instead of matching projectPath

Expected: When launching Claude Code in /path/to/project-b, the loader matches the project-scope entry whose projectPath equals the current CWD, and loads skills from that entry's installPath.

Actual: The loader picks installed_plugins.json["plugins"]["myplugin@mymarketplace"][0] regardless of CWD. Working in project B but loading project A's skills (the older, smaller set).

Repro:

  1. Two project-scope installs of the same plugin pinned to different commit SHAs (one with more skills than the other).
  2. Confirm in installed_plugins.json that project A's entry is at array index 0, project B's at index 1.
  3. cd /path/to/project-b && claude --print "list available skills". Skills reflect project A's commit.
  4. Swap the array order so project B's entry is at index 0. Re-run from project B. Skills now reflect project B's commit.

Bug 2: Project-scope installs do not auto-update

Expected: With autoUpdate: true on the marketplace, project-scope install records refresh when the marketplace updates, the same way user-scope records do.

Actual: Project-scope records pin to the install-time commit and never update. From my installed_plugins.json:

  • A user-scope entry: installedAt: 2026-04-29, lastUpdated: 2026-05-01 — auto-update working.
  • A project-scope entry installed the same day: installedAt: 2026-04-29, lastUpdated: 2026-04-29. Multiple commits pushed since. lastUpdated == installedAt.

/reload-plugins resolves the current commit during a session, but the change does not appear to persist to installed_plugins.json, so cold restart re-loads the stale install path.

Bug 3: /plugin UI Remove fails for project-scope entries

Expected: Selecting Remove on a project-scope plugin in the /plugin UI removes the project-scope record.

Actual: Two error states observed sequentially in the UI:

  1. The entry was shown with the message: Plugin "myplugin" not found in marketplace "mymarketplace". (The plugin clearly is in that marketplace — verified by reading marketplace.json on disk.)
  2. After confirming Remove: Plugin "myplugin@mymarketplace" is not installed in project scope. Use --scope to specify the correct scope.

Workaround: edit installed_plugins.json directly.

Combined effect

Bug 2 ensures any actively-iterated project-scope install goes stale. Bug 1 turns that stale-ness into broken loading in sibling projects, not just the project where the stale install lives — one stale entry poisons skill loading anywhere else the same plugin is installed at project scope. Bug 3 blocks the documented removal path.

Net result: project-scope is currently only viable for stable, rarely-changing plugins. For anything under active iteration, the only working path is user-scope + per-project enabledPlugins: false opt-out.

View original on GitHub ↗

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