MCPcopy Create free account
hub / github.com/WJX20/claude-code / execCommandHook

Function execCommandHook

src/utils/hooks.ts:829–1417  ·  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

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

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
logForDebuggingFunction · 0.85
getCachedPowerShellPathFunction · 0.85

Tested by

no test coverage detected