[Bug] Plugins enabled by project `enabledPlugins` load, but the Desktop plugins page and `claude plugin list` report them as not installed
Environment
- Claude Code 2.1.281, Claude desktop app (Code tab, local session) on Windows 11
- Project with a directory-source marketplace declared in the checked-in
.claude/settings.json
Setup
.claude/settings.json in the repository root:
{
"extraKnownMarketplaces": {
"y-specs": { "source": { "source": "directory", "path": "." } }
},
"enabledPlugins": {
"y-specs@y-specs": true,
"y-specs-extensions@y-specs": true,
"y-specs-fpa@y-specs": true,
"y-specs-github@y-specs": true
}
}
The repository root holds .claude-plugin/marketplace.json listing the four plugins (relative-path sources).
What happens
- The session loads all four plugins: their skills (for example
y-specs-github:github-sync) are available, and/reload-pluginsreports them. - The desktop app's plugins page says no plugins are installed for this workspace.
claude plugin list --json --available, run from the repository root, lists the enabled plugins underavailable, notinstalled:
``claude plugin install --scope project
installed y-specs-extensions@y-specs project (after a manual )``
available y-specs@y-specs
available y-specs-fpa@y-specs
available y-specs-github@y-specs
- The interactive
/pluginscreen in a terminalclaudesession shows the same plugins correctly.
The listing appears to read only ~/.claude/plugins/installed_plugins.json, which is written only by plugin install, not the enabledPlugins entries.
Expected
The plugins reference says plugins are stored in the enabledPlugins setting of the scope's settings file, and project scope "adds the plugin to .claude/settings.json". A plugin enabled at project scope in a trusted repository, and loaded by the session, should be listed as installed (project scope) in the desktop plugins page and in claude plugin list, consistently with the interactive /plugin screen.
Side issues seen while diagnosing
- The workaround,
claude plugin install <p>@<m> --scope project, rewrites.claude/settings.json(movesenabledPluginsandextraKnownMarketplacesto the end and reorders keys inside every hook entry), producing a noisy diff with no semantic change. - For a directory-source marketplace, the install still copies the plugin to
~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/, although the docs say such plugins load in place and are not copied to the cache. - Install records are keyed by
projectPath, so a desktop session running in a worktree (.claude/worktrees/<name>) does not match records made in the main checkout.