#!/usr/bin/env bash # hooklib dispatcher — routes one Claude Code hook event to every drop-in # script under hooks/.d/, in lexical order. # # Usage (from hooks.json): dispatch.sh # # The event JSON is read once from stdin and replayed to each sub-hook on # its own stdin. A sub-hook that wants to act prints a hook JSON response # (e.g. a PreToolUse permissionDecision) to stdout and exits 0. The FIRST # sub-hook to emit non-empty stdout wins: the dispatcher forwards that # response verbatim and stops. If no sub-hook emits anything, the dispatcher # stays silent and exits 0, which Claude Code treats as "no opinion / allow". # # Adding a hook is just dropping an executable script into the matching # hooks/.d/ directory — no wiring changes required. set -euo pipefail event="${1:?dispatch.sh needs an event name}" here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" dir="${here}/$(printf '%s' "$event" | tr '[:upper:]' '[:lower:]').d" payload="$(cat)" [ -d "$dir" ] || exit 0 for hook in "$dir"/*.sh; do [ -e "$hook" ] || continue out="$(printf '%s' "$payload" | bash "$hook" || true)" if [ -n "$out" ]; then printf '%s\n' "$out" exit 0 fi done exit 0