MCPcopy
hub / github.com/claude-code-best/claude-code / executeHooksOutsideREPL

Function executeHooksOutsideREPL

src/utils/hooks.ts:3140–3525  ·  view source on GitHub ↗

* Execute hooks outside of the REPL (e.g. notifications, session end) * * Unlike executeHooks() which yields messages that are exposed to the model as * system messages, this function only logs errors via logForDebugging (visible * with --debug). Callers that need to surface errors to users shou

({
  getAppState,
  hookInput,
  matchQuery,
  signal,
  timeoutMs = TOOL_HOOK_EXECUTION_TIMEOUT_MS,
}: {
  getAppState?: () => AppState
  hookInput: HookInput
  matchQuery?: string
  signal?: AbortSignal
  timeoutMs: number
})

Source from the content-addressed store, hash-verified

3138 * @returns Array of HookOutsideReplResult objects containing command, succeeded, and output
3139 */
3140async function executeHooksOutsideREPL({
3141 getAppState,
3142 hookInput,
3143 matchQuery,
3144 signal,
3145 timeoutMs = TOOL_HOOK_EXECUTION_TIMEOUT_MS,
3146}: {
3147 getAppState?: () => AppState
3148 hookInput: HookInput
3149 matchQuery?: string
3150 signal?: AbortSignal
3151 timeoutMs: number
3152}): Promise<HookOutsideReplResult[]> {
3153 if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) {
3154 return []
3155 }
3156
3157 const hookEvent = hookInput.hook_event_name
3158 const hookName = matchQuery ? `${hookEvent}:${matchQuery}` : hookEvent
3159 if (shouldDisableAllHooksIncludingManaged()) {
3160 logForDebugging(
3161 `Skipping hooks for ${hookName} due to 'disableAllHooks' managed setting`,
3162 )
3163 return []
3164 }
3165
3166 // SECURITY: ALL hooks require workspace trust in interactive mode
3167 // This centralized check prevents RCE vulnerabilities for all current and future hooks
3168 if (shouldSkipHookDueToTrust()) {
3169 logForDebugging(
3170 `Skipping ${hookName} hook execution - workspace trust not accepted`,
3171 )
3172 return []
3173 }
3174
3175 const appState = getAppState ? getAppState() : undefined
3176 // Use main session ID for outside-REPL hooks
3177 const sessionId = getSessionId()
3178 const matchingHooks = await getMatchingHooks(
3179 appState,
3180 sessionId,
3181 hookEvent,
3182 hookInput,
3183 )
3184 if (matchingHooks.length === 0) {
3185 return []
3186 }
3187
3188 if (signal?.aborted) {
3189 return []
3190 }
3191
3192 const userHooks = matchingHooks.filter(h => !isInternalHook(h))
3193 if (userHooks.length > 0) {
3194 const pluginHookCounts = getPluginHookCounts(userHooks)
3195 const hookTypeCounts = getHookTypeCounts(userHooks)
3196 logEvent(`tengu_run_hook`, {
3197 hookName:

Callers 12

executeNotificationHooksFunction · 0.85
executeStopFailureHooksFunction · 0.85
executePreCompactHooksFunction · 0.85
executePostCompactHooksFunction · 0.85
executeSessionEndHooksFunction · 0.85
executeConfigChangeHooksFunction · 0.85
executeEnvHooksFunction · 0.85
executeElicitationHooksFunction · 0.85

Calls 15

shouldSkipHookDueToTrustFunction · 0.85
getSessionIdFunction · 0.85
getMatchingHooksFunction · 0.85
isInternalHookFunction · 0.85
getPluginHookCountsFunction · 0.85
getHookTypeCountsFunction · 0.85
logEventFunction · 0.85
jsonStringifyFunction · 0.85
isAsyncHookJSONOutputFunction · 0.85
isSyncHookJSONOutputFunction · 0.85

Tested by

no test coverage detected