(
source: 'startup' | 'resume' | 'clear' | 'compact',
{
sessionId,
agentType,
model,
forceSyncExecution,
}: SessionStartHooksOptions = {},
)
| 33 | |
| 34 | // Note to CLAUDE: do not add ANY "warmup" logic. It is **CRITICAL** that you do not add extra work on startup. |
| 35 | export async function processSessionStartHooks( |
| 36 | source: 'startup' | 'resume' | 'clear' | 'compact', |
| 37 | { |
| 38 | sessionId, |
| 39 | agentType, |
| 40 | model, |
| 41 | forceSyncExecution, |
| 42 | }: SessionStartHooksOptions = {}, |
| 43 | ): Promise<HookResultMessage[]> { |
| 44 | // --bare skips all hooks. executeHooks already early-returns under --bare |
| 45 | // (hooks.ts:1861), but this skips the loadPluginHooks() await below too — |
| 46 | // no point loading plugin hooks that'll never run. |
| 47 | if (isBareMode()) { |
| 48 | return [] |
| 49 | } |
| 50 | const hookMessages: HookResultMessage[] = [] |
| 51 | const additionalContexts: string[] = [] |
| 52 | const allWatchPaths: string[] = [] |
| 53 | |
| 54 | // Skip loading plugin hooks if restricted to managed hooks only |
| 55 | // Plugin hooks are untrusted external code that should be blocked by policy |
| 56 | if (shouldAllowManagedHooksOnly()) { |
| 57 | logForDebugging('Skipping plugin hooks - allowManagedHooksOnly is enabled') |
| 58 | } else { |
| 59 | // Ensure plugin hooks are loaded before executing SessionStart hooks. |
| 60 | // loadPluginHooks() may be called early during startup (fire-and-forget, non-blocking) |
| 61 | // to pre-load hooks, but we must guarantee hooks are registered before executing them. |
| 62 | // This function is memoized, so if hooks are already loaded, this returns immediately |
| 63 | // with negligible overhead (just a cache lookup). |
| 64 | try { |
| 65 | await withDiagnosticsTiming('load_plugin_hooks', () => loadPluginHooks()) |
| 66 | } catch (error) { |
| 67 | // Log error but don't crash - continue with session start without plugin hooks |
| 68 | /* eslint-disable no-restricted-syntax -- both branches wrap with context, not a toError case */ |
| 69 | const enhancedError = |
| 70 | error instanceof Error |
| 71 | ? new Error( |
| 72 | `Failed to load plugin hooks during ${source}: ${error.message}`, |
| 73 | ) |
| 74 | : new Error( |
| 75 | `Failed to load plugin hooks during ${source}: ${String(error)}`, |
| 76 | ) |
| 77 | /* eslint-enable no-restricted-syntax */ |
| 78 | |
| 79 | if (error instanceof Error && error.stack) { |
| 80 | enhancedError.stack = error.stack |
| 81 | } |
| 82 | |
| 83 | logError(enhancedError) |
| 84 | |
| 85 | // Provide specific guidance based on error type |
| 86 | const errorMessage = |
| 87 | error instanceof Error ? error.message : String(error) |
| 88 | let userGuidance = '' |
| 89 | |
| 90 | if ( |
| 91 | errorMessage.includes('Failed to clone') || |
| 92 | errorMessage.includes('network') || |
no test coverage detected