CwdChanged
Runs when a shell command in the main conversation changes the working directory, for example when Claude executes a cd command. Use this to react to directory changes: reload environment variables, activate project-specific toolchains, or run setup scripts automatically. Pairs with FileChanged for tools like direnv that manage per-directory environment.
CwdChanged hooks have access to CLAUDE_ENV_FILE. Variables written to that file persist into subsequent Bash commands until the next CwdChanged event, when Claude Code clears them.
CwdChanged doesn't support matchers and fires on every occurrence.
CwdChanged input
In addition to the common input fields, CwdChanged hooks receive old_cwd and new_cwd.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
"cwd": "/Users/my-project/src",
"hook_event_name": "CwdChanged",
"old_cwd": "/Users/my-project",
"new_cwd": "/Users/my-project/src"
}CwdChanged output
In addition to the JSON output fields available to all hooks, CwdChanged hooks can return watchPaths to dynamically set which file paths FileChanged watches:
| Field | Description |
|---|---|
watchPaths | Array of absolute paths. Replaces the current dynamic watch list. Paths from your matcher configuration are always watched. Returning an empty array clears the dynamic list, which is typical when entering a new directory |
CwdChanged hooks have no decision control. They can't block the directory change.
Claude Code reads watchPaths and systemMessage from their JSON output and discards continue. In interactive sessions, it shows the systemMessage as a brief terminal notification. The message doesn't reach the SDK message stream.