Project-scope plugin installs are auto-created, never updated, and unreachable via --scope project (Windows drive-case duplicates)

Status Open
Maintainer reply None cached
Activity 1 comment · opened Sep 4, 2026

What happens

A plugin installed at user scope also gets a project-scope install created automatically the first time a session starts in a project whose .claude/settings.json declares it in enabledPlugins. That project-scope entry is pinned to whatever version was current at creation time and is never updated afterwards, so installed_plugins.json accumulates one stale entry per project.

After a few weeks across seven projects, claude plugin list showed eight entries for a single plugin — one usable user-scope entry and seven stale project-scope ones spanning three different versions.

Why it is hard to clean up

  1. claude plugin update only moves user scope. Every project-scope entry keeps its original version indefinitely — all of mine had installedAt == lastUpdated.
  1. --scope project does not resolve to the current directory. Run from project B, claude plugin update <plugin> --scope project updated the entry for project A instead, then reported "already at the latest version" on subsequent runs. claude plugin uninstall <plugin> --scope project run from a project that demonstrably has a project-scope entry failed with:

> Plugin "<plugin>" is installed in user scope, not project. Use --scope user to uninstall.

  1. Windows drive-letter casing creates duplicate entries for the same project. Some entries recorded projectPath as d:\dev\<project> and others as D:\dev\<project>. The CLI appears to match projectPath as an exact string, so the lower-case ones became unreachable — it could neither see nor remove them — and a second entry was created for a project that already had one. This is why the count grows rather than settling at one per project.

The only way I found to remove them was editing ~/.claude/plugins/installed_plugins.json by hand.

Impact

Low severity but persistent. In my case user scope won at load time, so nothing ran a stale version — the entries were inert clutter. But the mechanism means a project could be pinned to an old plugin version with no supported way to update or remove it, and claude plugin list becomes unreadable.

There is also a second-order effect worth noting: a plugin that ships a SessionStart hook comparing a "current" value it carries against project state will report stale information silently and correctly, because the reference value comes from whichever plugin version loaded. My plugin's staleness alarm stayed quiet on a genuinely out-of-date project for exactly this reason, and only started reporting correctly once user scope was updated.

Expected

  • claude plugin update should update project-scope installs too, or at least offer a way to.
  • --scope project should resolve to the current working directory's project.
  • projectPath comparison should be case-insensitive on Windows so one project cannot hold two entries.
  • Ideally, a project-scope install should not be created at all when the same plugin is already enabled at user scope at an equal or newer version.

Environment

  • Windows 11, Claude Code CLI (plugin marketplace via a private git marketplace)
  • Reproduced across seven local projects between 2026-08-31 and 2026-09-03

Notes

Removing the enabledPlugins declaration from the projects' .claude/settings.json would stop the entries being created, but that declaration is how teammates get the plugin at all, so it is not a workaround we can use.

View original on GitHub ↗

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