Skip to content

Commit 46ca687

Browse files
bojanrajkovicclaude
andcommitted
fix(tui): filter installed plugins by current project before merging
The CLI returns all installed plugins globally, including ones from other projects. Previously, buildInstalledByID included all of them, so processAvailablePlugin would merge installed data (Enabled, Version, InstallPath) from other projects, making available plugins incorrectly appear as "installed, disabled" in the current project. Add filterRelevantInstalled to exclude installed plugins that don't belong to the current project before building the ID map. A plugin is relevant if it's user-scoped, its projectPath matches workingDir, or it appears in any settings file for the current project. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5587597 commit 46ca687

2 files changed

Lines changed: 24 additions & 3 deletions

File tree

internal/tui/CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Implements the two-pane plugin manager interface using Bubble Tea's Elm Architec
99
## Contracts
1010

1111
- **Exposes**: `NewModel(client, workingDir) -> *Model`, implements `tea.Model` interface
12-
- **Guarantees**: Pending operations (install/uninstall/enable/disable/scope-change) tracked until explicit Apply. Filter preserves selection when possible. Only project/local plugins for current workingDir are shown (plus user-scoped plugins).
12+
- **Guarantees**: Pending operations (install/uninstall/enable/disable/scope-change) tracked until explicit Apply. Filter preserves selection when possible. All installed plugins are shown regardless of which project they were installed from; scope data reflects the current workingDir.
1313
- **Expects**: Valid `claude.Client` implementation. Terminal with reasonable size (handles resize).
1414

1515
## Dependencies

internal/tui/model.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,8 @@ func (m *Model) loadPlugins() tea.Msg {
401401
// Only installed plugins relevant to workingDir are included.
402402
func mergePlugins(list *claude.PluginList, workingDir string) []PluginState {
403403
allScopes := claude.GetAllEnabledPlugins(workingDir)
404-
installedByID := buildInstalledByID(list.Installed)
404+
relevant := filterRelevantInstalled(list.Installed, workingDir, allScopes)
405+
installedByID := buildInstalledByID(relevant)
405406
seenInstalled := make(map[string]bool)
406407
byMarketplace := make(map[string][]PluginState)
407408

@@ -412,7 +413,7 @@ func mergePlugins(list *claude.PluginList, workingDir string) []PluginState {
412413
}
413414

414415
// Process installed plugins not in available list
415-
for _, p := range list.Installed {
416+
for _, p := range relevant {
416417
state, ok := processInstalledPlugin(p, allScopes, seenInstalled)
417418
if ok {
418419
byMarketplace[state.Marketplace] = append(byMarketplace[state.Marketplace], state)
@@ -422,6 +423,26 @@ func mergePlugins(list *claude.PluginList, workingDir string) []PluginState {
422423
return sortAndGroupByMarketplace(byMarketplace)
423424
}
424425

426+
// filterRelevantInstalled returns only installed plugins relevant to the current project.
427+
// A plugin is relevant if it's user-scoped, installed in the current workingDir,
428+
// or appears in any settings file for the current project.
429+
func filterRelevantInstalled(plugins []claude.InstalledPlugin, workingDir string, allScopes claude.ScopeState) []claude.InstalledPlugin {
430+
var result []claude.InstalledPlugin
431+
for _, p := range plugins {
432+
switch {
433+
case p.Scope == claude.ScopeUser:
434+
result = append(result, p)
435+
case p.ProjectPath == workingDir:
436+
result = append(result, p)
437+
default:
438+
if _, inSettings := allScopes[p.ID]; inSettings {
439+
result = append(result, p)
440+
}
441+
}
442+
}
443+
return result
444+
}
445+
425446
// buildInstalledByID creates a map of installed plugins by ID.
426447
func buildInstalledByID(installed []claude.InstalledPlugin) map[string]claude.InstalledPlugin {
427448
result := make(map[string]claude.InstalledPlugin)

0 commit comments

Comments
 (0)