Files
claude-plugin-hooklib/README.md
T

2.0 KiB

hooklib

A small library of composable Claude Code hooks. Each hook is a drop-in script; a shared router dispatches one event to all of them. Adding a hook is just dropping an executable script into the matching directory — no wiring.

Layout

hooks/
  hooks.json            # registers the router per event (currently PreToolUse:Bash)
  dispatch.sh           # router: feeds the event JSON to every hooks/<event>.d/*.sh
  pretooluse.d/
    10-nix-deploy-output.sh

How dispatch works

dispatch.sh <Event> reads the event JSON from stdin once and replays it to each script in hooks/<event>.d/ (lexical order). A sub-hook that wants to act prints a hook JSON response to stdout and exits 0; the first sub-hook to emit non-empty output wins and the router forwards it verbatim. If nobody speaks up, the router is silent (Claude Code treats that as "allow").

Scripts are numbered (10-, 20-, …) to make ordering explicit.

Hooks

nix-deploy-output (PreToolUse / Bash)

Problem: nix run .#deploy (deploy-rs) shows empty output in Claude Code. deploy-rs and nix render progress as an ANSI spinner on a TTY and log to stderr; captured non-interactively, that collapses to a blank.

Fix: the command must fold stderr into stdout and defeat TTY detection so the tools emit plain, line-buffered logs. The canonical form is:

nix run .#deploy 2>&1 | cat

The hook inspects any nix run .#deploy… command. If it is already in the output-safe form (2>&1 and a pipe into cat/tee/less) it passes through untouched. Otherwise it denies the call and returns the corrected command (preserving any -- flags / host target) for Claude to run instead.

Adding another hook

Drop hooks/<event>.d/NN-name.sh (executable) that reads the event JSON on stdin and prints a hook response only when it wants to intervene. If the event isn't PreToolUse, add it to hooks/hooks.json pointing at dispatch.sh <Event>.

Install (local dev)

claude plugin install hooklib@oleks-local