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": {