The path an event takes
Claude Code supports hooks: commands it runs when something happens (a prompt is sent, a tool is about to run, a permission is needed). Nook installs hooks for twelve events, and each one runs a tiny program:
Claude Code -> nook-hook.exe (relay) -> named pipe (local) -> Nook island
- Claude Code runs
nook-hook.exewith the event name and passes the event as JSON on standard input. - The relay adds a little terminal context (which terminal or editor the session runs in, so the island can bring the right window forward) and hands the event to Nook over the named pipe
\\.\pipe\nook-<your user SID>. Only your own account can open that pipe. - Nook updates the island. For a permission request or a question, it waits for your click and the relay prints the answer for Claude Code.
The twelve events are SessionStart, SessionEnd, UserPromptSubmit, PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, Notification, Stop, StopFailure, SubagentStart and SubagentStop.
What is written to settings.json
Nook adds one entry per event to the hooks section of %USERPROFILE%\.claude\settings.json. Each looks like this:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "C:/Users/you/AppData/Local/Nook/bin/nook-hook.exe",
"args": ["Stop"],
"timeout": 10
}
]
}
]
}
}
Timeouts are 10 seconds for most events and 120 seconds for PermissionRequest, which waits for a human. The path of your relay will differ. Nothing else in the file is changed.
Why the exec form?
The entries use Claude Code’s exec form: command is just the relay’s path and args holds the event name, so no shell is involved. An older style wrote a quoted command string such as "C:/.../nook-hook.exe" Stop. That only worked in Git Bash. On a PC without Git Bash, Claude Code runs hooks in PowerShell, which reads a quoted path as a string and fails with an error like Unexpected token 'Stop' (a ParserError). The exec form avoids this, and spaces in a path stop mattering. If you installed with an older Nook, reinstall the hooks and the entries are rewritten. A Claude Code too old to know args still runs the relay, which then reads the event name from the JSON on standard input.
How the install stays safe
Your settings file is yours, so every change goes through the same steps:
- Read. Nook reads the file. If it cannot be read or is not valid JSON, Nook refuses and tells you; it never treats an unreadable file as empty and writes over it.
- Preview. It shows the exact diff.
- Click. Nothing is written until you confirm. The file’s fingerprint is checked at that moment, so if another tool or your editor changed it in between, Nook stops and shows a fresh diff.
- Backup. The file is copied beside itself as
settings.json.bak-YYYYMMDD-HHMMSSbefore anything changes. - Atomic write. The new file is written next to the original and renamed over it, so a crash or a full disk cannot leave half a file.
Hooks that belong to other tools are never touched. If entries left by Coucou (the project Nook is based on) are present, Settings says so and installing replaces them with Nook’s.
How to remove the hooks
Open Settings → Claude Code and click Uninstall hooks…. It follows the same preview, backup and click, and removes Nook’s entries only. Do this before uninstalling the app, because the uninstaller deliberately does not edit settings.json. If the relay is gone, a leftover entry usually costs nothing beyond a dead path, but removing it keeps the file tidy. You can also restore a .bak file by hand.
What the relay never does
- It never blocks Claude Code. The relay gives Nook 300 milliseconds to answer the connection. If Nook is closed, slow or crashed, the relay exits with nothing on standard output and Claude Code carries on as if Nook were not installed. Only a permission request waits for a human, and even then only up to about 110 seconds, after which Claude Code asks in the terminal.
- It never approves anything. An answer is printed only after you click Allow or Deny. No answer means no output, which means Claude Code asks you itself.
- It never uses the network. The relay talks to one local pipe and nothing else. Before sending anything it checks that it is talking to Nook: same user,
nook.exe, no lower integrity level.
To show what an event is about, the relay reads a little context (the tail of the session’s transcript for its title and last message, the file an edit targets, command output). That goes only to the app over the local pipe and stays in memory. The log records event names and decisions, never content.
The optional usage status line
Claude Code hands its status line command a JSON that includes the 5-hour and weekly limits. Installing Usage limits in Settings sets the statusLine key in settings.json so that command is nook-hook statusline. It follows the same preview, backup and click.
- Chaining. If you already have a status line, its whole setting is stored, encoded, after
--previousin the new command. The relay runs it on every call and prints what it prints, so it keeps showing. Uninstalling puts it back exactly as it was. With none, uninstalling removes the key. - No quoting.
statusLinehas noargs, so the command is read by a shell (Git Bash or PowerShell on Windows), and the two quote differently. Nook therefore writes a command with nothing to quote. A relay path that contains a space or a special character is refused rather than guessed at. See troubleshooting. - Freshness. Claude Code reports limits only on subscription plans and only while a session runs, so the numbers are “as of the last Claude Code activity”.
Check what the relay sees
nook-hook --where is a diagnostic. It prints, as JSON, the terminal context and the parent processes the relay would report from the place you run it. It reads no input and sends nothing to Nook. Run it from the terminal where a session runs:
& "$env:LOCALAPPDATA\Nook\bin\nook-hook.exe" --whereUse it when the “go to session” button goes to the wrong window, or when you want to confirm the relay exists. More in troubleshooting.