59 lines
2.0 KiB
Markdown
59 lines
2.0 KiB
Markdown
# 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
|
|
```
|