Session and Environment Hooks
- Configure SessionStart hooks to load environment variables, set the session title, and register watch paths
- Use PreCompact and PostCompact hooks to preserve and restore critical state across context compaction
- Implement a reactive environment loading pattern with FileChanged and CwdChanged hooks
The Session Lifecycle: Start to End
A Claude Code session has a lifecycle that begins before any tool calls and continues after the last response. Session-cadence hooks fire at the boundaries of this lifecycle, giving you control over what happens when a session starts, when the context compacts, and when the session ends.
These hooks are fundamentally different from tool-cadence hooks. Instead of reacting to individual operations, they set up and tear down the environment in which all operations run. Getting the environment right at session start means every tool call that follows has the right context, the right variables, and the right configuration.
SessionStart: Environment and Context Setup
The SessionStart event fires when any session begins. The matcher field controls which session origins trigger the hook.
startup fires when a completely new session opens. This is the right time to load project-specific environment variables, verify that required tools are installed, and send any notification that a new work session has begun.
resume fires when a previously paused session is resumed. You may want to refresh environment variables that could have changed (API tokens, feature flags) without re-running the full initialization from startup.
compact fires after a context compaction, when the session continues from a compact-resumed state. This is distinct from a full resume — the session is the same session, the context was just summarized.
clear fires after a /clear command resets the conversation. The project and environment are unchanged, but the conversation history is empty.
A SessionStart hook can return several special fields in its JSON output. sessionTitle sets the terminal window or tab title. initialUserMessage injects a message that Claude sees at the start of the session, as if the user typed it — useful for loading a daily standup prompt or a maintenance checklist automatically. watchPaths registers additional directories for FileChanged events beyond those Claude Code monitors by default. reloadSkills forces a skill refresh when set to true.
The $CLAUDE_ENV_FILE environment variable points to a temporary file that persists environment variables across hook runs within the session. A SessionStart hook can write export KEY=value lines to this file, and those variables will be available in subsequent hooks and tool calls throughout the session. This is the correct way to load secrets from a vault or values from a project-specific .envrc file.
PreCompact and PostCompact: Surviving Context Compaction
In long sessions, Claude Code automatically compacts the context — summarizing the conversation history to free up context window space. This compaction is necessary for sessions that run for hours, but it can lose important state that was in the conversation but not in any file.
PreCompact fires immediately before the context is summarized. A hook can inject additionalContext into the compaction, telling the summarizer what to preserve. Think of it as writing a note to the summarizer: "Make sure to include the fact that we decided to use the event-sourcing approach" or "Preserve the current status of the migration: 3 of 7 tables done."
The additionalContext returned by a PreCompact hook is included in the input to the compaction model. Well-crafted PreCompact hooks dramatically improve the quality of compact summaries by ensuring that in-progress decisions, partially completed tasks, and session-specific context survive the compaction.
PostCompact fires after compaction completes. The session continues, but the conversation history is now a summary. A PostCompact hook can reload context that may have been lost — re-reading key files, re-registering watch paths, or sending an initialUserMessage-style prompt to orient Claude in the compact-resumed state.
Together, PreCompact and PostCompact let you build sessions that run for hours without losing critical state to compaction — a significant quality-of-life improvement for long autonomous workflows.
FileChanged and CwdChanged: Reactive Environments
Claude Code can watch files for changes and fire hooks when they change. The FileChanged event works like a filesystem trigger — when a watched file is modified, the hook fires with the filename and change type.
To use FileChanged, you must first register the directories to watch. Add a watchPaths array to a SessionStart hook's output, listing the directories Claude Code should monitor. Files matching the FileChanged hook's matcher within those directories will then trigger the hook when changed.
The classic use case is direnv-style environment loading. When .envrc changes, a FileChanged hook runs the appropriate loader to update the environment. This keeps environment variables in sync with the project configuration without requiring you to manually reload them after every change.
CwdChanged fires when the working directory changes — when Claude Code navigates between directories using shell commands. A CwdChanged hook can detect the new directory and load its environment configuration automatically, enabling seamless transitions between different project configurations within the same session.
SessionEnd, InstructionsLoaded, and ConfigChange
SessionEnd fires when the session closes. Use it for cleanup: deleting temporary files, flushing audit logs, sending a session-completion notification, or recording session statistics. Unlike most events, SessionEnd cannot block or modify anything — by the time it fires, the session is closing regardless.
InstructionsLoaded fires when CLAUDE.md files or other rule files are loaded. The matcher can filter by load reason. This is useful if you want to react to project-specific instructions being applied — for example, logging which CLAUDE.md files were loaded for audit purposes.
ConfigChange fires when Claude Code detects a change to a settings file. It can block the change from taking effect by exiting with code 2. A ConfigChange hook can validate that the proposed settings change meets organizational policy before applying it — preventing developers from accidentally disabling security hooks through settings edits.
- SessionStart fires with a source matcher — target startup for new sessions, resume for continued ones, compact for post-compaction restarts, clear for /clear resets to run appropriate initialization at each stage
- $CLAUDE_ENV_FILE persists environment variables across hook runs — append export statements to this file in a SessionStart hook to make them available in subsequent hooks and tool calls throughout the session
- PreCompact fires before context is summarized — inject additionalContext to tell the compaction model what to preserve, preventing critical in-progress state from being lost in long sessions
- PostCompact fires after compaction completes — use it to reload context that the summary may have dropped, re-register watch paths, or orient Claude in the resumed state
- FileChanged requires watchPaths registration — add directory paths to watchPaths in a SessionStart hook output or the FileChanged event will not fire for files in those locations