source dump of claude code
at main 133 lines 5.1 kB view raw
1import { resetSdkInitState } from '../../bootstrap/state.js' 2import { isRestrictedToPluginOnly } from '../settings/pluginOnlyPolicy.js' 3// Import as module object so spyOn works in tests (direct imports bypass spies) 4import * as settingsModule from '../settings/settings.js' 5import { resetSettingsCache } from '../settings/settingsCache.js' 6import type { HooksSettings } from '../settings/types.js' 7 8let initialHooksConfig: HooksSettings | null = null 9 10/** 11 * Get hooks from allowed sources. 12 * If allowManagedHooksOnly is set in policySettings, only managed hooks are returned. 13 * If disableAllHooks is set in policySettings, no hooks are returned. 14 * If disableAllHooks is set in non-managed settings, only managed hooks are returned 15 * (non-managed settings cannot disable managed hooks). 16 * Otherwise, returns merged hooks from all sources (backwards compatible). 17 */ 18function getHooksFromAllowedSources(): HooksSettings { 19 const policySettings = settingsModule.getSettingsForSource('policySettings') 20 21 // If managed settings disables all hooks, return empty 22 if (policySettings?.disableAllHooks === true) { 23 return {} 24 } 25 26 // If allowManagedHooksOnly is set in managed settings, only use managed hooks 27 if (policySettings?.allowManagedHooksOnly === true) { 28 return policySettings.hooks ?? {} 29 } 30 31 // strictPluginOnlyCustomization: block user/project/local settings hooks. 32 // Plugin hooks (registered channel, hooks.ts:1391) are NOT affected — 33 // they're assembled separately and the managedOnly skip there is keyed 34 // on shouldAllowManagedHooksOnly(), not on this policy. Agent frontmatter 35 // hooks are gated at REGISTRATION (runAgent.ts:~535) by agent source — 36 // plugin/built-in/policySettings agents register normally, user-sourced 37 // agents skip registration under ["hooks"]. A blanket execution-time 38 // block here would over-kill plugin agents' hooks. 39 if (isRestrictedToPluginOnly('hooks')) { 40 return policySettings?.hooks ?? {} 41 } 42 43 const mergedSettings = settingsModule.getSettings_DEPRECATED() 44 45 // If disableAllHooks is set in non-managed settings, only managed hooks still run 46 // (non-managed settings cannot override managed hooks) 47 if (mergedSettings.disableAllHooks === true) { 48 return policySettings?.hooks ?? {} 49 } 50 51 // Otherwise, use all hooks (merged from all sources) - backwards compatible 52 return mergedSettings.hooks ?? {} 53} 54 55/** 56 * Check if only managed hooks should run. 57 * This is true when: 58 * - policySettings has allowManagedHooksOnly: true, OR 59 * - disableAllHooks is set in non-managed settings (non-managed settings 60 * cannot disable managed hooks, so they effectively become managed-only) 61 */ 62export function shouldAllowManagedHooksOnly(): boolean { 63 const policySettings = settingsModule.getSettingsForSource('policySettings') 64 if (policySettings?.allowManagedHooksOnly === true) { 65 return true 66 } 67 // If disableAllHooks is set but NOT from managed settings, 68 // treat as managed-only (non-managed hooks disabled, managed hooks still run) 69 if ( 70 settingsModule.getSettings_DEPRECATED().disableAllHooks === true && 71 policySettings?.disableAllHooks !== true 72 ) { 73 return true 74 } 75 return false 76} 77 78/** 79 * Check if all hooks (including managed) should be disabled. 80 * This is only true when managed/policy settings has disableAllHooks: true. 81 * When disableAllHooks is set in non-managed settings, managed hooks still run. 82 */ 83export function shouldDisableAllHooksIncludingManaged(): boolean { 84 return ( 85 settingsModule.getSettingsForSource('policySettings')?.disableAllHooks === 86 true 87 ) 88} 89 90/** 91 * Capture a snapshot of the current hooks configuration 92 * This should be called once during application startup 93 * Respects the allowManagedHooksOnly setting 94 */ 95export function captureHooksConfigSnapshot(): void { 96 initialHooksConfig = getHooksFromAllowedSources() 97} 98 99/** 100 * Update the hooks configuration snapshot 101 * This should be called when hooks are modified through the settings 102 * Respects the allowManagedHooksOnly setting 103 */ 104export function updateHooksConfigSnapshot(): void { 105 // Reset the session cache to ensure we read fresh settings from disk. 106 // Without this, the snapshot could use stale cached settings when the user 107 // edits settings.json externally and then runs /hooks - the session cache 108 // may not have been invalidated yet (e.g., if the file watcher's stability 109 // threshold hasn't elapsed). 110 resetSettingsCache() 111 initialHooksConfig = getHooksFromAllowedSources() 112} 113 114/** 115 * Get the current hooks configuration from snapshot 116 * Falls back to settings if no snapshot exists 117 * @returns The hooks configuration 118 */ 119export function getHooksConfigFromSnapshot(): HooksSettings | null { 120 if (initialHooksConfig === null) { 121 captureHooksConfigSnapshot() 122 } 123 return initialHooksConfig 124} 125 126/** 127 * Reset the hooks configuration snapshot (useful for testing) 128 * Also resets SDK init state to prevent test pollution 129 */ 130export function resetHooksConfigSnapshot(): void { 131 initialHooksConfig = null 132 resetSdkInitState() 133}