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

Function execCommandHook

src/utils/hooks.ts:830–1472  ·  view source on GitHub ↗

* Execute a command-based hook using bash or PowerShell. * * Shell resolution: hook.shell → 'bash'. PowerShell hooks spawn pwsh * with -NoProfile -NonInteractive -Command and skip bash-specific prep * (POSIX path conversion, .sh auto-prepend, CLAUDE_CODE_SHELL_PREFIX). * See docs/design/ps-shel

(
  hook: HookCommand & { type: 'command' },
  hookEvent: HookEvent | 'StatusLine' | 'FileSuggestion',
  hookName: string,
  jsonInput: string,
  signal: AbortSignal,
  hookId: string,
  hookIndex?: number,
  pluginRoot?: string,
  pluginId?: string,
  skillRoot?: string,
  forceSyncExecution?: boolean,
  requestPrompt?: (request: PromptRequest) => Promise<PromptResponse>,
)

Source from the content-addressed store, hash-verified

828 * See docs/design/ps-shell-selection.md §5.1.
829 */
830async function execCommandHook(
831 hook: HookCommand & { type: 'command' },
832 hookEvent: HookEvent | 'StatusLine' | 'FileSuggestion',
833 hookName: string,
834 jsonInput: string,
835 signal: AbortSignal,
836 hookId: string,
837 hookIndex?: number,
838 pluginRoot?: string,
839 pluginId?: string,
840 skillRoot?: string,
841 forceSyncExecution?: boolean,
842 requestPrompt?: (request: PromptRequest) => Promise<PromptResponse>,
843): Promise<{
844 stdout: string
845 stderr: string
846 output: string
847 status: number
848 aborted?: boolean
849 backgrounded?: boolean
850}> {
851 // Gated to once-per-session events to keep diag_log volume bounded.
852 // started/completed live inside the try/finally so setup-path throws
853 // don't orphan a started marker — that'd be indistinguishable from a hang.
854 const shouldEmitDiag =
855 hookEvent === 'SessionStart' ||
856 hookEvent === 'Setup' ||
857 hookEvent === 'SessionEnd'
858 const diagStartMs = Date.now()
859 let diagExitCode: number | undefined
860 let diagAborted = false
861
862 const isWindows = getPlatform() === 'windows'
863
864 // --
865 // Per-hook shell selection (phase 1 of docs/design/ps-shell-selection.md).
866 // Resolution order: hook.shell → DEFAULT_HOOK_SHELL. The defaultShell
867 // fallback (settings.defaultShell) is phase 2 — not wired yet.
868 //
869 // The bash path is the historical default and stays unchanged. The
870 // PowerShell path deliberately skips the Windows-specific bash
871 // accommodations (cygpath conversion, .sh auto-prepend, POSIX-quoted
872 // SHELL_PREFIX).
873 const shellType = hook.shell ?? DEFAULT_HOOK_SHELL
874
875 const isPowerShell = shellType === 'powershell'
876
877 // --
878 // Windows bash path: hooks run via Git Bash (Cygwin), NOT cmd.exe.
879 //
880 // This means every path we put into env vars or substitute into the command
881 // string MUST be a POSIX path (/c/Users/foo), not a Windows path
882 // (C:\Users\foo or C:/Users/foo). Git Bash cannot resolve Windows paths.
883 //
884 // windowsPathToPosixPath() is pure-JS regex conversion (no cygpath shell-out):
885 // C:\Users\foo -> /c/Users/foo, UNC preserved, slashes flipped. Memoized
886 // (LRU-500) so repeated calls are cheap.
887 //

Callers 4

executeHooksFunction · 0.85
executeHooksOutsideREPLFunction · 0.85
executeStatusLineCommandFunction · 0.85

Calls 15

getPlatformFunction · 0.85
getProjectRootFunction · 0.85
pathExistsFunction · 0.85
getPluginDataDirFunction · 0.85
formatShellPrefixCommandFunction · 0.85
subprocessEnvFunction · 0.85
getHookEnvFilePathFunction · 0.85
getCwdFunction · 0.85
getOriginalCwdFunction · 0.85
getCachedPowerShellPathFunction · 0.85
buildPowerShellArgsFunction · 0.85

Tested by

no test coverage detected