Procedure
- wrapper script 設置:
~/.claude/scripts/hook-latency-wrap.sh <real_hook_path> "$@"で stdin を保持しつつ実 hook を exec 前後で$EPOCHREALTIMEを取得。差分を µs 精度で~/.claude/logs/hook-latency.jsonlに追記 (ts/hook/duration_ms/exit_code)。 - settings.json の配線書き換え: 該当 hook の
commandを~/.claude/scripts/hook-latency-wrap.sh ~/.claude/hooks/<real>.shに差し替え。複数 hook 同時 wrap 可能。 - stdin 透過: wrapper は
cat | <real_hook>ではなくexec直前まで stdin を保持し、実 hook に丸ごと渡す (Claude Code hook protocol を壊さない)。 - 失敗時 passthrough: 実 hook の exit code をそのまま返す。wrap 自体は失敗しても
exit 0で hook chain を止めない。 - レポート集計:
~/.claude/scripts/hook-latency-report.shで過去 7d を hook 別に p50/p95/max/fail rate に集計。
Pitfalls
- bash 5+ の
$EPOCHREALTIMEを使うこと。macOS 標準 bash 3.2 では未定義 →/opt/homebrew/bin/bashshebang を明示する。python3 を spawn すると 30-50ms オーバーヘッドが追加で乗る (本末転倒)。 - wrap 自体のオーバーヘッドは ~20ms。1ms 単位の精度を求めるなら別途差し引き必要。
exec前に>>で log に追記すると実 hook の stdout/stderr を巻き込む → 計測ログは別 fd (e.g.exec 9>>...) か wrap 終了後に書く。stop_notify.shのような非同期 hook は wrap の終了タイミングと実完了がズレる場合あり。fire-and-forget なら計測無意味。
Verification
bash -n ~/.claude/scripts/hook-latency-wrap.shで構文 OK。- 試験 hook で
echo '{}' | ~/.claude/scripts/hook-latency-wrap.sh ~/.claude/hooks/<target>.sh実行 → log に entry 1 行追加。 - 通常 hook 発火 (Edit/Stop 等) 後に
tail ~/.claude/logs/hook-latency.jsonlで記録確認。 hook-latency-report.shで p95 が想定範囲 (< 500ms 推奨) に収まっている。