import { useCallback, useEffect } from 'react' import type { Command } from '../commands.js' import { clearCommandMemoizationCaches, clearCommandsCache, getCommands, } from '../commands.js' import { onGrowthBookRefresh } from '../services/analytics/growthbook.js' import { logError } from '../utils/log.js' import { skillChangeDetector } from '../utils/skills/skillChangeDetector.js' /** * Keep the commands list fresh across two triggers: * * 1. Skill file changes (watcher) — full cache clear + disk re-scan, since * skill content changed on disk. * 2. GrowthBook init/refresh — memo-only clear, since only `isEnabled()` * predicates may have changed. Handles commands like /btw whose gate * reads a flag that isn't in the disk cache yet on first session after * a flag rename: getCommands() runs before GB init (main.tsx:2855 vs * showSetupScreens at :3106), so the memoized list is baked with the * default. Once init populates remoteEvalFeatureValues, re-filter. */ export function useSkillsChange( cwd: string | undefined, onCommandsChange: (commands: Command[]) => void, ): void { const handleChange = useCallback(async () => { if (!cwd) return try { // Clear all command caches to ensure fresh load clearCommandsCache() const commands = await getCommands(cwd) onCommandsChange(commands) } catch (error) { // Errors during reload are non-fatal - log and continue if (error instanceof Error) { logError(error) } } }, [cwd, onCommandsChange]) useEffect(() => skillChangeDetector.subscribe(handleChange), [handleChange]) const handleGrowthBookRefresh = useCallback(async () => { if (!cwd) return try { clearCommandMemoizationCaches() const commands = await getCommands(cwd) onCommandsChange(commands) } catch (error) { if (error instanceof Error) { logError(error) } } }, [cwd, onCommandsChange]) useEffect( () => onGrowthBookRefresh(handleGrowthBookRefresh), [handleGrowthBookRefresh], ) }