From f8bd3ba87012b517cca1439e242216437d76b4c9 Mon Sep 17 00:00:00 2001 From: Toray Altas Date: Sat, 17 Jan 2026 10:43:22 -0500 Subject: [PATCH] feat: enable continuous hooks configuration reload Move the periodic hooks configuration reload from the HooksSettings component to the main App component. This ensures that hooks are reloaded every 5 seconds whenever the feature is enabled, regardless of which settings tab is currently active. --- webview-ui/src/App.tsx | 11 ++++ webview-ui/src/__tests__/App.spec.tsx | 63 ++++++++++++++++++++ webview-ui/src/i18n/locales/en/settings.json | 2 +- 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index cccb0422ca..ae29e830d7 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -66,8 +66,19 @@ const App = () => { cloudOrganizations, renderContext, mdmCompliant, + experiments, } = useExtensionState() + // Hooks auto-reload + useEffect(() => { + if (experiments?.hooks) { + const intervalId = setInterval(() => { + vscode.postMessage({ type: "hooksReloadConfig" }) + }, 5000) + return () => clearInterval(intervalId) + } + }, [experiments?.hooks]) + // Create a persistent state manager const marketplaceStateManager = useMemo(() => new MarketplaceViewStateManager(), []) diff --git a/webview-ui/src/__tests__/App.spec.tsx b/webview-ui/src/__tests__/App.spec.tsx index e8e08782da..00666e8ed9 100644 --- a/webview-ui/src/__tests__/App.spec.tsx +++ b/webview-ui/src/__tests__/App.spec.tsx @@ -4,6 +4,7 @@ import React from "react" import { render, screen, act, cleanup } from "@/utils/test-utils" import AppWithProviders from "../App" +import { vscode } from "@src/utils/vscode" vi.mock("@src/utils/vscode", () => ({ vscode: { @@ -172,6 +173,68 @@ describe("App", () => { }) }) + it("auto-reloads hooks config every 5 seconds when hooks experiment is enabled", () => { + vi.useFakeTimers() + + mockUseExtensionState.mockReturnValue({ + didHydrateState: true, + showWelcome: false, + shouldShowAnnouncement: false, + experiments: { hooks: true }, + language: "en", + telemetrySetting: "enabled", + }) + + const { unmount } = render() + + // Initial render shouldn't trigger reload (it's triggered by effect interval) + expect(vscode.postMessage).not.toHaveBeenCalledWith({ type: "hooksReloadConfig" }) + + // Advance time by 5s + act(() => { + vi.advanceTimersByTime(5000) + }) + expect(vscode.postMessage).toHaveBeenCalledWith({ type: "hooksReloadConfig" }) + + // Advance time another 5s + act(() => { + vi.advanceTimersByTime(5000) + }) + expect(vscode.postMessage).toHaveBeenCalledTimes(3) // 1 initial (webviewDidLaunch) + 2 repeats + + // Unmount and verify no more calls + unmount() + act(() => { + vi.advanceTimersByTime(5000) + }) + expect(vscode.postMessage).toHaveBeenCalledTimes(3) + + vi.useRealTimers() + }) + + it("does not auto-reload hooks config when hooks experiment is disabled", () => { + vi.useFakeTimers() + + mockUseExtensionState.mockReturnValue({ + didHydrateState: true, + showWelcome: false, + shouldShowAnnouncement: false, + experiments: { hooks: false }, + language: "en", + telemetrySetting: "enabled", + }) + + render() + + // Advance time by 5s + act(() => { + vi.advanceTimersByTime(5000) + }) + expect(vscode.postMessage).not.toHaveBeenCalledWith({ type: "hooksReloadConfig" }) + + vi.useRealTimers() + }) + afterEach(() => { cleanup() window.removeEventListener("message", () => {}) diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 2fe9c694f7..d1f80392a6 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -903,7 +903,7 @@ }, "HOOKS": { "name": "Enable Hooks", - "description": "Use custom shell commands to automate actions before or after tool execution." + "description": "Use custom shell commands to automate actions before or after tool execution. (Restart required after saving settings)" } }, "promptCaching": {