[BUG] Project-scoped plugin enabled in multiple repos never loads on first session after switching repos (single install record, re-stamped after plugin load)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code (2.1.199)
What's Wrong?
When the same plugin is enabled via checked-in .claude/settings.json (extraKnownMarketplaces + enabledPlugins) in two or more repos, the plugin silently fails to load in the first session after every repo switch — all of its skills, agents, hooks, and MCP servers are absent. If you alternate between the repos (the normal workflow the team-onboarding feature is designed for), the plugin effectively never loads.
Three interacting behaviors cause this:
~/.claude/plugins/installed_plugins.jsonkeeps a single project-scoped entry per plugin, with oneprojectPath. Multi-project enablement is not representable.- The plugin loader loads a project-scoped plugin only when
record.projectPath === cwd. - The startup sync ("Syncing installed_plugins.json with enabledPlugins from all settings.json files" / "settings.json is source of truth") re-stamps the record's
projectPathto the current project — but it runs after plugins have already been loaded.
So a session in repo B finds the record pointing at repo A → skips the plugin → then "repairs" the record to B, which breaks the next session in repo A. Ping-pong.
Steps to Reproduce
- Two repos,
project-aandproject-b, each with an identical checked-in.claude/settings.json:
``json``
{
"extraKnownMarketplaces": {
"my-marketplace": { "source": { "source": "github", "repo": "org/claude-plugin" } }
},
"enabledPlugins": { "my-plugin@my-marketplace": true }
}
- Install/trust the plugin from
project-a(registry entry becomes{"scope": "project", "projectPath": ".../project-a"}). cd project-a && claude --debug-file /tmp/a1.log -p "ok"→ log showsLoaded N skills from plugin my-plugin. ✅cd project-b && claude --debug-file /tmp/b1.log -p "ok"→ noLoaded N skills from plugin my-pluginline, no plugin skills/agents/hooks in the session. ❌ RegistryprojectPathis now.../project-b.cd project-b && claude ...again → loads. ✅ (record now matches)cd project-a && claude ...→ missing again. ❌
Evidence from --debug-file
Ordering inside a single startup — load happens before the sync that would have fixed the record:
[DEBUG] Loaded 32 installed plugins from ~/.claude/plugins/installed_plugins.json
[DEBUG] Attempting to load skills from plugin my-plugin default skillsPath: ... <-- gated by stale projectPath
[DEBUG] Loaded 28 skills from plugin my-plugin <-- only when projectPath matches cwd
[DEBUG] Syncing installed_plugins.json with enabledPlugins from all settings.json files <-- runs AFTER load
[DEBUG] Updated my-plugin@my-marketplace scope to project (settings.json is source of truth)
The re-stamp is observable as lastUpdated/projectPath changing in installed_plugins.json at session start, one session too late.
Notes
- User-level
enabledPluginsdoes not stabilize it: with both user and project settings enabling the plugin, the sync's desired state resolves toprojectscope withprojectPath = cwd, so the record keeps flipping. - Manually adding two project entries to the registry array (one per repo) survives sessions in the repo whose entry is first, but a session in the other repo rewrites the first entry to the current project and the dedupe pass collapses the now-identical entries back to one.
- Related but distinct: #45997 (closed) — loader taking
[0]from a multi-entry array; here the sync actively collapses to a single entry so the wrong-version symptom becomes a not-loaded-at-all symptom. #63663 (open, "enabledPlugins not persisting across restarts") looks like the same root cause observed without the multi-repo diagnosis.
Expected Behavior
A plugin enabled by the current project's settings should load regardless of which project a shared single install record was last stamped with. Either keep one install record per (plugin, projectPath), or run the settings→registry sync before plugin loading, or have the loader treat "enabled in current project's settings + present in cache" as loadable.
Environment
- Claude Code 2.1.199 (native install), macOS 14 (arm64)
- Plugin: private GitHub marketplace, enabled via checked-in project settings in 3 repos
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗