Project-scope plugin installs: loader picks wrong array entry, no auto-update, broken UI removal
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:
- Two project-scope installs of the same plugin pinned to different commit SHAs (one with more skills than the other).
- Confirm in
installed_plugins.jsonthat project A's entry is at array index 0, project B's at index 1. cd /path/to/project-b && claude --print "list available skills". Skills reflect project A's commit.- 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:
- The entry was shown with the message:
Plugin "myplugin" not found in marketplace "mymarketplace". (The plugin clearly is in that marketplace — verified by readingmarketplace.jsonon disk.) - 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.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗