(operation: 'enable' | 'disable' | 'update' | 'uninstall')
| 996 | |
| 997 | // Handle single plugin operations from details view |
| 998 | const handleSingleOperation = async (operation: 'enable' | 'disable' | 'update' | 'uninstall') => { |
| 999 | if (!selectedPlugin) return; |
| 1000 | const pluginScope = selectedPlugin.scope || 'user'; |
| 1001 | const isBuiltin = pluginScope === 'builtin'; |
| 1002 | |
| 1003 | // Built-in plugins can only be enabled/disabled, not updated/uninstalled. |
| 1004 | if (isBuiltin && (operation === 'update' || operation === 'uninstall')) { |
| 1005 | setProcessError('Built-in plugins cannot be updated or uninstalled.'); |
| 1006 | return; |
| 1007 | } |
| 1008 | |
| 1009 | // Managed scope plugins can only be updated, not enabled/disabled/uninstalled |
| 1010 | if (!isBuiltin && !isInstallableScope(pluginScope) && operation !== 'update') { |
| 1011 | setProcessError('This plugin is managed by your organization. Contact your admin to disable it.'); |
| 1012 | return; |
| 1013 | } |
| 1014 | setIsProcessing(true); |
| 1015 | setProcessError(null); |
| 1016 | try { |
| 1017 | const pluginId_3 = `${selectedPlugin.plugin.name}@${selectedPlugin.marketplace}`; |
| 1018 | let reverseDependents: string[] | undefined; |
| 1019 | |
| 1020 | // enable/disable omit scope — pluginScope is the install scope from |
| 1021 | // installed_plugins.json (where files are cached), which can diverge |
| 1022 | // from the settings scope (where enablement lives). Passing it trips |
| 1023 | // the cross-scope guard. Auto-detect finds the right scope. #38084 |
| 1024 | switch (operation) { |
| 1025 | case 'enable': |
| 1026 | { |
| 1027 | const enableResult = await enablePluginOp(pluginId_3); |
| 1028 | if (!enableResult.success) { |
| 1029 | throw new Error(enableResult.message); |
| 1030 | } |
| 1031 | break; |
| 1032 | } |
| 1033 | case 'disable': |
| 1034 | { |
| 1035 | const disableResult = await disablePluginOp(pluginId_3); |
| 1036 | if (!disableResult.success) { |
| 1037 | throw new Error(disableResult.message); |
| 1038 | } |
| 1039 | reverseDependents = disableResult.reverseDependents; |
| 1040 | break; |
| 1041 | } |
| 1042 | case 'uninstall': |
| 1043 | { |
| 1044 | if (isBuiltin) break; // guarded above; narrows pluginScope |
| 1045 | if (!isInstallableScope(pluginScope)) break; |
| 1046 | // If the plugin is enabled in .claude/settings.json (shared with the |
| 1047 | // team), divert to a confirmation dialog that offers to disable in |
| 1048 | // settings.local.json instead. Check the settings file directly — |
| 1049 | // `pluginScope` (from installed_plugins.json) can be 'user' even when |
| 1050 | // the plugin is ALSO project-enabled, and uninstalling the user-scope |
| 1051 | // install would leave the project enablement active. |
| 1052 | if (isPluginEnabledAtProjectScope(pluginId_3)) { |
| 1053 | setIsProcessing(false); |
| 1054 | setViewState('confirm-project-uninstall'); |
| 1055 | return; |
no test coverage detected