📖 Lesson content
There are more hooks beyond the PreToolUse and PostToolUse hooks discussed in this course. There are also:
Notification- Runs when Claude Code sends a notification, which occurs when Claude needs permission to use a tool, or after Claude Code has been idle for 60 secondsStop- Runs when Claude Code has finished respondingSubagentStop- Runs when a subagent (these are displayed as a "Task" in the UI) has finishedPreCompact- Runs before a compact operation occurs, either manual or automaticUserPromptSubmit- Runs when the user submits a prompt, before Claude processes itSessionStart- Runs when starting or resuming a sessionSessionEnd- Runs when a session ends
Here's the confusing part:
- The stdin input to your commands will change based upon the type of hook being executed (
PreToolUse,PostToolUse,Notification, etc) - The
tool_inputcontained in that will differ based upon the tool that was called (in the case ofPreToolUseandPostToolUsehooks)
For example, here's a sample of some stdin input to a hook, where the hook is a PostToolUse that was watching for uses of the TodoWrite tool. For reference, that is the tool that Claude uses to keep track of to-do items.
{
"session_id": "9ecf22fa-edf8-4332-ae85-b6d5456eda64",
"transcript_path": "<path_to_transcript>",
"hook_event_name": "PostToolUse",
"tool_name": "TodoWrite",
"tool_input": {
"todos": [{ "content": "write a readme", "status": "pending", "priority": "medium", "id": "1" }]
},
"tool_response": {
"oldTodos": [],
"newTodos": [{ "content": "write a readme", "status": "pending", "priority": "medium", "id": "1" }]
}
}
And for comparison, here's an example of the input to a Stop hook:
{
"session_id": "af9f50b6-f042-4773-b3e2-c3a4814765ce",
"transcript_path": "<path_to_transcript>",
"hook_event_name": "Stop",
"stop_hook_active": false
}
As you can see, the stdin input to your command will differ significantly based upon the hook (PreToolUse, PostToolUse, Stop, etc) and the matcher used (in the case of PreToolUse and PostToolUse). This can make writing hooks challenging - you might not know the exact structure of the input to your command!
To handle this challenge, try making a helper hook like this:
"PostToolUse": [ // Or "PreToolUse" or "Stop", etc
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "jq . > post-log.json"
}
]
},
]
Notice the provided command. It will write the input to this hook to the post-log.json file, which allows you to inspect exactly what would have been fed into your command! This makes it a lot easier for you to understand what data your command should inspect.
🔁 Related lessons
- Next: The Claude Code SDK
- Previous: Useful hooks!
- Part of paths: Path C
- Reference docs: Glossary · Skills atlas · By use-case
📚 Source & attribution
- Original Anthropic Academy lesson: https://anthropic.skilljar.com/claude-code-in-action/312427
- © 2025 Anthropic. Educational fair-use only.