Skip to content

feat: plugin scope management with multi-scope support#31

Merged
bojanrajkovic merged 20 commits intomainfrom
brajkovic/plugin-scope-mgmt
Feb 15, 2026
Merged

feat: plugin scope management with multi-scope support#31
bojanrajkovic merged 20 commits intomainfrom
brajkovic/plugin-scope-mgmt

Conversation

@bojanrajkovic
Copy link
Contributor

@bojanrajkovic bojanrajkovic commented Feb 14, 2026

Summary

  • Fix InstallPlugin/UninstallPlugin to use correct CLI commands (install/uninstall instead of enable/disable)
  • Add GetAllEnabledPlugins() to read all three settings files (user, project, local) for authoritative multi-scope detection
  • Migrate PluginState and Operation structs to support multiple scopes (InstalledScopes map, Scopes slice)
  • Render multi-scope indicators in list view ([USER, LOCAL]), details pane, and confirmation modal
  • Make l/p/u/e/Tab keys context-aware: single-scope plugins get existing behavior, multi-scope opens scope dialog
  • Add S key scope dialog modal with checkbox UI for user/project/local, delta computation generating OpInstall/OpUninstall/OpScopeChange
  • Smart execution engine: uses install vs enable and uninstall vs disable based on settings file state

🤖 Generated with Claude Code

bojanrajkovic and others added 17 commits February 14, 2026 11:28
InstallPlugin() now shells out to 'claude plugin install' instead of 'enable'
UninstallPlugin() now shells out to 'claude plugin uninstall' instead of 'disable'
EnablePlugin() and DisablePlugin() remain unchanged with correct commands
Add GetAllEnabledPlugins and ScopeState type to read all three settings
files (user, project, local) and accumulate scope sets per plugin ID.
Missing settings files are handled gracefully. Each scope's bool value
indicates enabled (true) or disabled-but-present (false).
Add comprehensive tests for GetAllEnabledPlugins covering all acceptance criteria:
- AC2.1: User-scoped plugins from ~/.claude/settings.json
- AC2.2: Project and local scopes from .claude/settings.json and .claude/settings.local.json
- AC2.3: Missing files handled gracefully with empty result
- AC2.4: Plugins appearing in multiple scopes return all scope entries
- Additional test for disabled-but-present plugins

All tests use temp directories and internal getAllEnabledPlugins for full
coverage of all three scope paths.
Fix shadow variable warnings from golangci-lint by using assignment (err =)
instead of short-form declaration (err :=) when reusing error variable in
subsequent operations. This maintains the original error variable from the
outer scope.
Change PluginState.InstalledScope to InstalledScopes map and
Operation.Scope/OriginalScope to Scopes slice/OriginalScopes map.
Add helper methods (IsInstalled, HasScope, IsSingleScope, SingleScope)
to minimize churn at call sites.
Update mergePlugins to use GetAllEnabledPlugins for settings-based
scope detection. Remove dead GetProjectEnabledPlugins function.
Add firstScope and copyMap helpers for scope map utilities.

Verifies:
- plugin-scope-mgmt.AC3.1: InstalledScopes maps accurately reflect all scopes
- plugin-scope-mgmt.AC3.2: Helper methods work correctly for all scope configs
- plugin-scope-mgmt.AC3.3: Operation.Scopes slice supports multi-scope operations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Migrate all test code to use new InstalledScopes map and Operation
Scopes/OriginalScopes fields. Update assertions to use helper methods
(IsInstalled, HasScope, SingleScope). Add new test TestPluginStateHelpers
to verify all helper method combinations.

Test coverage:
- TestPluginStateHelpers: Verify IsInstalled, HasScope, IsSingleScope,
  SingleScope work for empty, single, and multi-scope cases
- All existing tests updated and passing

Verifies:
- plugin-scope-mgmt.AC3.1: PluginStateFromInstalled/FromAvailable tests
- plugin-scope-mgmt.AC3.2: New TestPluginStateHelpers test
- plugin-scope-mgmt.AC3.3: Operation-related tests with new fields

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add scope formatting helpers: scopeOrder, scopeLabel, formatScopeSet for consistent formatting
- Update getScopeIndicator to display multi-scope plugins (e.g., [USER, LOCAL])
- Update renderPendingIndicator to handle multi-scope operations with scope transitions
- Handle OpScopeChange operation type in display functions
- Update status text and pending change messages for multi-scope display
- Update confirmation modal and progress display for multi-scope operations
- Replace Unicode arrows with ASCII for consistency

Verifies: plugin-scope-mgmt.AC4.1, plugin-scope-mgmt.AC4.2

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Modify WithDimensions() in styles.go to accommodate wider multi-scope indicators.
The left pane now uses max(width/3-4, 25) to ensure a minimum width of 25 chars,
which is sufficient for worst-case multi-scope displays like [USER, PROJECT, LOCAL].

Verifies plugin-scope-mgmt.AC4.3: Left pane width adjusts dynamically for longer
scope indicators.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement Phase 5 of plugin scope management:
- Add ModeScopeDialog mode constant for scope selection dialog
- Add scopeDialogState struct to hold dialog UI state
- Add openScopeDialog() helper to transition to scope dialog mode
- Update selectForInstall() to branch on IsSingleScope()
- Update selectForUninstall() to branch on IsSingleScope()
- Update toggleEnablement() to branch on IsSingleScope()
- Update toggleScope() (Tab key) to no-op on multi-scope plugins
- Add minimal Update handler for ModeScopeDialog that allows Esc to exit

Verifies AC5 acceptance criteria:
- AC5.1: selectForInstall opens dialog for multi-scope, creates op for single-scope
- AC5.2: selectForUninstall opens dialog for multi-scope, creates op for single-scope
- AC5.3: toggleEnablement opens dialog for multi-scope, creates op for single-scope
- AC5.4: toggleScope (Tab) is no-op for multi-scope, works for single-scope

All tests pass (78 total, including 8 new multi-scope tests).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements Phase 6 scope dialog feature:
- Task 1: Add S key binding for scope dialog (Shift+S)
- Task 2: Implement full dialog input handler with up/down navigation, space toggle for checkboxes, Enter to apply delta, Esc to cancel
- Task 3: Render dialog as centered overlay with scope names and settings file paths

Changes:
- Add Scope field to KeyBindings struct with S key binding
- Add updateScopeDialog() to handle keyboard input in ModeScopeDialog mode
- Add applyScopeDialogDelta() to compute delta and generate pending operations (OpInstall, OpUninstall, OpScopeChange)
- Add renderScopeDialog() to display dialog with checkboxes, cursor, and file paths
- Wire scope dialog into Update and View functions
- Add openScopeDialogForSelected() helper method

Verification:
- AC6.1: S key opens dialog with pre-checked boxes for installed scopes
- AC6.2: Space toggles checkbox, Enter applies changes, Esc cancels
- AC6.3: Delta correctly produces OpInstall/OpUninstall/OpScopeChange
- AC6.4: Dialog shows settings file paths for each scope
- Comprehensive tests verify all functionality

Test results: 15 new tests added, all passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement Phase 7 smart execution for multi-scope operations with settings-aware
CLI command selection. Updates executeOperation() to loop over op.Scopes and
choose the correct CLI command (install vs enable, uninstall vs disable) based on
whether the plugin exists in the settings file.

Changes:
- executeOperation() now reads settings via GetAllEnabledPlugins() once at start
- For OpInstall: calls EnablePlugin if plugin exists in settings, else InstallPlugin
- For OpUninstall: calls UninstallPlugin if plugin exists, else DisablePlugin
- All multi-scope operations loop over scopes and stop on first error
- Error messages include the failing scope name for better debugging
- OpScopeChange now integrated into operation sort order (after OpMigrate)
- startExecution() sort order updated: Uninstall(0), Migrate(1), ScopeChange(2),
  Update(3), Install(4), Enable(5), Disable(6)

Verifies plugin-scope-mgmt.AC7:
- AC7.1: Smart install selection (enable vs install)
- AC7.2: Smart uninstall selection (uninstall vs disable)
- AC7.3: Multi-scope error handling with scope reporting
- AC7.4: Operation ordering maintained with OpScopeChange

Tests added:
- TestExecuteOperationInstallWhenNotInSettings: AC7.1 - InstallPlugin path
- TestExecuteOperationInstallWhenInSettings: AC7.1 - EnablePlugin path
- TestExecuteOperationUninstallWhenInSettings: AC7.2 - UninstallPlugin path
- TestExecuteOperationMultiScopeStopsOnFirstError: AC7.3 - Error handling
- TestStartExecutionOperationOrdering: AC7.4 - Operation sort order

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reuse the allScopes/pluginScopes variables already read at the top
of executeOperation instead of reading settings again.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update CLAUDE.md files to reflect multi-scope plugin support:
- TUI now uses InstalledScopes map instead of single InstalledScope
- Operation.Scopes replaces Operation.Scope for multi-scope targets
- New OpScopeChange operation type and ModeScopeDialog mode
- Dependency on GetAllEnabledPlugins (replaces GetProjectEnabledPlugins)
- S key binding for scope dialog documented

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cator, and minimum left pane width

Add comprehensive tests for three acceptance criteria:

- AC4.1: formatScopeSet tests
  - TestFormatScopeSet_SingleScope: Verifies single scope output (e.g., "USER")
  - TestFormatScopeSet_MultiScope: Verifies multi-scope output (e.g., "USER, LOCAL")
  - TestFormatScopeSet_Ordering: Verifies canonical ordering (USER, PROJECT, LOCAL)

- AC4.2: renderPendingIndicator tests
  - TestRenderPendingIndicator_PartialUninstall: Verifies partial uninstall transition display
  - TestRenderPendingIndicator_ScopeChange: Verifies scope change transition display
  - TestRenderPendingIndicator_MultiScopeInstall: Verifies multi-scope install display

- AC4.3: Minimum left pane width test
  - TestWithDimensions_MinimumLeftPaneWidth: Verifies left pane width >= 25 on narrow terminals

All tests verify actual behavior using text fragment matching for styled strings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@bojanrajkovic
Copy link
Contributor Author

Test Plan

  • All automated tests pass (go test -race -count=1 ./...)
  • Single-scope plugins display correct scope indicator
  • Multi-scope plugins display combined indicator (e.g., [USER, LOCAL])
  • S key opens scope dialog with pre-checked installed scopes
  • Space toggles checkboxes, Enter applies, Esc cancels
  • Delta computation produces correct pending operations
  • Apply executes operations in correct order with smart CLI command selection

Full human test plan: docs/test-plans/2026-02-14-plugin-scope-mgmt.md

bojanrajkovic and others added 3 commits February 14, 2026 12:47
Extract helpers to reduce cyclomatic complexity in executeOperation,
renderPendingIndicator, appendPendingChange, and renderProgress. Fix
struct field alignment, unused test parameters, and if-else chain.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reorder Model struct fields for optimal GC pointer bitmap layout,
reducing pointer bytes from 11040 to 11008.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract runPluginCommand() helper in client.go, reducing 4 identical
  CLI methods to one-liners delegating to a shared implementation
- Add OperationType.meta() method centralizing display metadata (verb,
  noun, pending prefix) and simplify 4 switch statements in view.go
- Add testModel() test helper reducing 4-line setup boilerplate across
  19 test functions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@bojanrajkovic bojanrajkovic merged commit 5ed219d into main Feb 15, 2026
8 checks passed
@bojanrajkovic bojanrajkovic deleted the brajkovic/plugin-scope-mgmt branch February 15, 2026 01:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments