The best way to understand hooks is to watch one fire. A PreToolUse hook runs just before Claude uses a tool, which makes it perfect for a first experiment: log every Bash command before it runs.
The config
Add this to your settings.json. The matcher "Bash" means the hook only fires when Claude is about to use the Bash tool:
{
"hooks": {
"PreToolUse": [
{ "matcher": "Bash",
"hooks": [{ "type": "command", "command": "cat >> ~/claude-bash.log" }] }
]
}
}
What just happened
When Claude is about to run a Bash command, Claude Code runs your hook first. The hook is a command type, so it runs in a shell. The important detail: the event details arrive as JSON on the command's standard input. Here cat >> ~/claude-bash.log simply appends that JSON to a log file.
Open the log after a session and you will see one JSON blob per Bash call, describing the tool and its input. That JSON is the raw material every hook works with.
Reading the data
Once you can capture the input, you can parse it. A common pattern is to pipe stdin through jq:
jq -r '.tool_input.command'
That pulls just the command string out of the event so your hook can decide what to do with it.
Why start here
A logging hook is safe. It reads the event and writes a file, but it never blocks anything, so you can add it without changing how your sessions behave. Once you are comfortable seeing the data flow in, the next steps, formatting and blocking, are small changes on top of the same foundation.
Comments
Be the first to comment.