({
enabled = true,
}: {
enabled?: boolean
} = {})
| 35 | * mental model. See Outline: declarative-settings-hXHBMDIf4b PR 5c. |
| 36 | */ |
| 37 | export function useManagePlugins({ |
| 38 | enabled = true, |
| 39 | }: { |
| 40 | enabled?: boolean |
| 41 | } = {}) { |
| 42 | const setAppState = useSetAppState() |
| 43 | const needsRefresh = useAppState(s => s.plugins.needsRefresh) |
| 44 | const { addNotification } = useNotifications() |
| 45 | |
| 46 | // Initial plugin load. Runs once on mount. NOT used for refresh — all |
| 47 | // post-mount refresh goes through /reload-plugins → refreshActivePlugins(). |
| 48 | // Unlike refreshActivePlugins, this also runs delisting enforcement and |
| 49 | // flagged-plugin notifications (session-start concerns), and does NOT bump |
| 50 | // mcp.pluginReconnectKey (MCP effects fire on their own mount). |
| 51 | const initialPluginLoad = useCallback(async () => { |
| 52 | try { |
| 53 | // Load all plugins - capture errors array |
| 54 | const { enabled, disabled, errors } = await loadAllPlugins() |
| 55 | |
| 56 | // Detect delisted plugins, auto-uninstall them, and record as flagged. |
| 57 | await detectAndUninstallDelistedPlugins() |
| 58 | |
| 59 | // Notify if there are flagged plugins pending dismissal |
| 60 | const flagged = getFlaggedPlugins() |
| 61 | if (Object.keys(flagged).length > 0) { |
| 62 | addNotification({ |
| 63 | key: 'plugin-delisted-flagged', |
| 64 | text: 'Plugins flagged. Check /plugins', |
| 65 | color: 'warning', |
| 66 | priority: 'high', |
| 67 | }) |
| 68 | } |
| 69 | |
| 70 | // Load commands, agents, and hooks with individual error handling |
| 71 | // Errors are added to the errors array for user visibility in Doctor UI |
| 72 | let commands: Command[] = [] |
| 73 | let agents: AgentDefinition[] = [] |
| 74 | |
| 75 | try { |
| 76 | commands = await getPluginCommands() |
| 77 | } catch (error) { |
| 78 | const errorMessage = |
| 79 | error instanceof Error ? error.message : String(error) |
| 80 | errors.push({ |
| 81 | type: 'generic-error', |
| 82 | source: 'plugin-commands', |
| 83 | error: `Failed to load plugin commands: ${errorMessage}`, |
| 84 | }) |
| 85 | } |
| 86 | |
| 87 | try { |
| 88 | agents = await loadPluginAgents() |
| 89 | } catch (error) { |
| 90 | const errorMessage = |
| 91 | error instanceof Error ? error.message : String(error) |
| 92 | errors.push({ |
| 93 | type: 'generic-error', |
| 94 | source: 'plugin-agents', |
no test coverage detected