Public/Read-ClaudeHookInput.ps1
|
function Read-ClaudeHookInput { <# .SYNOPSIS Reads and parses the JSON payload that Claude Code sends to a hook script on stdin. .DESCRIPTION In production (no -InputString), reads [Console]::In to end. In tests, pass -InputString to avoid reading actual stdin. Rejects payloads over 10MB. Throws on invalid JSON unless -Quiet is specified. .PARAMETER InputString JSON string to parse instead of reading stdin. Used for Pester testing. .PARAMETER Quiet Returns $null instead of throwing when JSON is invalid or input is empty. .EXAMPLE # In a hook script's main block: $hook = Read-ClaudeHookInput $hook.tool_name # e.g. 'Bash' $hook.tool_input.command # e.g. 'rm -rf /' Reads stdin from Claude and accesses the parsed event properties. .EXAMPLE # In a Pester test: $hook = Read-ClaudeHookInput -InputString '{"tool_name":"Bash","tool_input":{"command":"ls"}}' $hook.tool_name | Should -Be 'Bash' Uses -InputString to inject a test payload without touching real stdin. .OUTPUTS System.Management.Automation.PSCustomObject .LINK about_ClaudeHooks .LINK https://code.claude.com/docs/en/hooks.md #> [OutputType([pscustomobject])] [CmdletBinding()] param( [string]$InputString, [switch]$Quiet ) $maxBytes = 10MB if ($PSBoundParameters.ContainsKey('InputString')) { $raw = $InputString } else { $raw = [Console]::In.ReadToEnd() } if ([string]::IsNullOrWhiteSpace($raw)) { if ($Quiet) { return $null } throw 'Hook input was empty.' } if ([System.Text.Encoding]::UTF8.GetByteCount($raw) -gt $maxBytes) { if ($Quiet) { return $null } throw 'Hook input exceeds maximum allowed size of 10MB.' } try { $raw | ConvertFrom-Json -Depth 64 } catch { if ($Quiet) { return $null } throw "Hook input is not valid JSON: $_" } } |