Hooks

Optional. braids works without them, and says which state it is in on every screen.

What a hook adds

Files cannot tell you the difference between a session that is running and one that has stopped to ask your permission. Both look like a transcript that has not changed in a while. A hook can, because the harness tells it.

With the hook installed, the map's state column separates stopped, needs you from working from idle, and n walks between the ones waiting on you. Without it, braids falls back to what the transcript can prove: whose turn was last, and how long ago. The Hooks: line in the header says which you are getting.

Installing and removing

braids hooks             # what is installed now
braids hooks --install
braids hooks --remove
braids hooks --json

This edits ~/.claude/settings.json and subscribes to six events: PermissionRequest, Notification, Stop, SubagentStop, SessionStart and SessionEnd. Use --settings to point at another file.

It merges. Hooks you already have on those events are left exactly where they are, and --remove takes back only the entries braids added, entry by entry.

If you installed the plugin, you already have these

The plugin registers the same six events, so running braids hooks --install on top of it leaves two hooks on every one. Claude Code runs a plugin's hooks alongside the ones in your settings file and does not notice they are the same tool, so both fire and every event is recorded twice.

Nothing looks wrong when this happens, which is the awkward part. Waiting states are read from the newest event for a session, and a duplicate is the same event again, so the map stays correct. What grows is ~/.braids/events.jsonl, at twice the rate, for good. braids doctor reports it and names the copy that is safe to remove.

braids hooks --remove    # keep the plugin's, drop the ones in settings.json

Identified by program, not by path

A braids hook is recognised by the program name in its command, not by the full path. Otherwise a binary at ~/go/bin/braids and one built in a checkout are two different hooks: the repo build reports not installed while the installed one is reporting, and --install adds a second entry that does the same job.

braids hook is not for you

braids hook is the command the harness runs. It reads a JSON event on stdin and records it. Typed by hand it used to sit there waiting for input that was never coming, so it now refuses a terminal and says what it is for.

braids hook
# braids: hook reads an event on stdin and is run by Claude Code, not by hand
# (try: braids hooks)