Claude Code hooks
The FileChanged hook in Claude Code
FileChanged fires when a watched file changes on disk. Its matcher uses a narrower syntax than every other event.
This event only exists for files a SessionStart hook put on the watch list in the first place, so a FileChanged hook that never fires is almost always missing that other half, not broken on its own.
- When it fires
- When a file on the watch list changes on disk, whoever changed it.
- Can it block?
- No.
- Matcher
- Yes, on
literal filenames - Matcher examples
.envrc|.envpackage.json
What it receives
The event arrives as JSON on standard input. These are the fields worth reading.
| Field | Type | What it is |
|---|---|---|
file_path | string | The file that changed. |
session_id | string | Stable for the session. |
cwd | string | Working directory. |
transcript_path | string | Path to the transcript. |
Exit codes
The exit code is the decision. Anything the script writes to standard error on a blocking exit is what Claude gets told.
| Exit | What happens |
|---|---|
0 | Success. |
2 | No blocking behavior. |
other | Non-blocking. |
What it can return
Nothing returned changes anything. Pair this with the watchPaths field a SessionStart hook can return, which is how files get onto the watch list in the first place.
No decision fields.
Noticing when dependencies move under a running session
A long session holds a picture of the project from when it started. If something changes package.json underneath it, the session does not find out until something breaks.
In settings.json
{
"hooks": {
"FileChanged": [
{
"matcher": "package.json",
"hooks": [
{ "type": "command", "command": "node ~/.claude/hooks/deps-changed.mjs" }
]
}
]
}
}
The script
const input = JSON.parse(require('fs').readFileSync(0, 'utf8')) || {};
console.log(`${input.file_path} changed on disk. ` +
'Re-read it before relying on what you loaded earlier.');
process.exit(0);
Use a SessionStart hook returning watchPaths to decide which files raise this event.
The catch
The matcher on this event uses a narrower character set than the others: letters, digits, underscores and pipes only. A pattern that works elsewhere may not work here.