How to Use the Pre‑Compact Hook for Context‑Window Management in Claude‑Code
The Pre‑Compact hook runs just before Claude compacts the conversation context, allowing you to preserve session state, log compaction events, and annotate active session files before older messages are summarized.
The Pre‑Compact hook is a Claude‑Code extension point that intercepts the automatic context compaction process. When a Claude session reaches its configured context‑window limit, the system truncates older messages and replaces them with a compressed summary. By hooking into this moment, you can capture critical state that would otherwise be lost.
This guide walks through enabling, customizing, and verifying the Pre‑Compact hook using the official everything-claude-code repository configuration.
How the Pre‑Compact Hook Works
The hook executes immediately before Claude generates the context summary. Its core responsibilities include:
- Logging the compaction event for debugging and audit trails
- Annotating the active session file with a human‑readable timestamp marker
- Persisting custom state such as temporary files or environment snapshots
The implementation relies on three interconnected files: the global hooks.json configuration that registers the hook, the Node script that performs the work, and a shared utility library for file operations.
Hook Registration in hooks.json
Claude‑Code discovers hooks through the hooks.json file. The PreCompact entry wires the context‑window management logic:
// hooks/hooks.json – PreCompact section (lines 56-66)
"PreCompact": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/pre-compact.js\""
}
],
"description": "Save state before context compaction"
}
]
The matcher: "*" ensures this hook runs for all sessions. The ${CLAUDE_PLUGIN_ROOT} environment variable resolves to the plugin installation directory, making the path portable across installations.
Core Implementation in pre-compact.js
The main hook logic lives in scripts/hooks/pre-compact.js. This script performs six sequential operations:
| Step | Operation | Source Reference |
|---|---|---|
| 1 | Resolve sessions directory via getSessionsDir() |
utils.js:30-35 |
| 2 | Ensure directory exists with ensureDir(sessionsDir) |
utils.js:54-58 |
| 3 | Append timestamped entry to compaction-log.txt |
pre-compact.js:28-31 |
| 4 | Locate newest session file via findFiles(sessionsDir, '*.tmp') |
utils.js:96-101 |
| 5 | Insert markdown annotation block into session file | pre-compact.js:34-40 |
| 6 | Emit log message to stderr for Claude visibility | pre-compact.js:41 |
The hook uses log('[PreCompact] State saved before compaction') to provide real‑time feedback in Claude's interface.
Utility Functions in utils.js
The scripts/lib/utils.js library provides cross‑cutting functionality for hook implementations:
// Key utilities used by PreCompact hook
// Returns ~/.claude/sessions
getSessionsDir()
// Creates directories recursively if missing
ensureDir(dirPath)
// Finds files by glob pattern, sorted by modification time
findFiles(directory, pattern)
// Generates YYYY-MM-DD_HH-MM-SS formatted timestamp
getDateTimeString()
// CLI output helper that writes to stderr
log(message)
These utilities standardize path resolution and file operations across all Claude‑Code hooks.
Customizing the Pre‑Compact Hook
You can extend the default behavior to capture additional state. Below is a modified pre-compact.js that backs up a custom JSON state file:
// scripts/hooks/pre-compact.js – extended with state backup
const { copyFileSync, existsSync } = require('fs');
const path = require('path');
const { getSessionsDir, getHomeDir, getDateTimeString, log } = require('../lib/utils.js');
const sessionsDir = getSessionsDir();
const STATE_FILE = path.join(getHomeDir(), '.myapp/state.json');
// Default: log compaction event
// (existing lines 28-31 here)
// Custom: backup state file if present
if (existsSync(STATE_FILE)) {
const backupName = `state-backup-${getDateTimeString()}.json`;
const backupPath = path.join(sessionsDir, backupName);
copyFileSync(STATE_FILE, backupPath);
log(`[PreCompact] State file backed up to ${backupPath}`);
}
// Default: annotate session file
// (existing lines 34-41 here)
Place this logic after the compaction log entry and before the session file annotation.
Verifying Hook Execution
To confirm the Pre‑Compact hook fired correctly:
- Check the compaction log:
cat ~/.claude/sessions/compaction-log.txt
Expected output:
[2026-09-07 14:27:12] Context compaction triggered
- Inspect the annotated session file:
cat ~/.claude/sessions/$(ls -t ~/.claude/sessions/*.tmp | head -1)
Look for the inserted markdown block:
---
**[Compaction occurred at 14:27]** - Context was summarized
- Review Claude's output panel for the log message:
[PreCompact] State saved before compaction
Alternative Entry Point: Bash Wrapper
The repository includes a shell script alternative at hooks/memory-persistence/pre-compact.sh. This wrapper provides the same functionality for environments where Node is unavailable or for users preferring shell implementations. Both entry points use the same utils.js library through Node's child_process when needed.
Key Files Reference
| File | Purpose | Location |
|---|---|---|
hooks/hooks.json |
Hook registry and configuration | [hooks/hooks.json](https://github.com/WorldFlowAI/everything-claude-code/blob/main/hooks/hooks.json#L56-L66) |
scripts/hooks/pre-compact.js |
Main Node implementation | [scripts/hooks/pre-compact.js](https://github.com/WorldFlowAI/everything-claude-code/blob/main/scripts/hooks/pre-compact.js) |
hooks/memory-persistence/pre-compact.sh |
Bash alternative entry point | [hooks/memory-persistence/pre-compact.sh](https://github.com/WorldFlowAI/everything-claude-code/blob/main/hooks/memory-persistence/pre-compact.sh) |
scripts/lib/utils.js |
Shared utilities for file I/O and logging | [scripts/lib/utils.js](https://github.com/WorldFlowAI/everything-claude-code/blob/main/scripts/lib/utils.js) |
Summary
- The Pre‑Compact hook executes immediately before Claude summarizes conversation context, preventing silent data loss
- Enable it through the PreCompact entry in
hooks.json, which invokesscripts/hooks/pre-compact.js - The default implementation logs compaction events to
compaction-log.txtand annotates active session files with markdown timestamps - Extend the hook by modifying
pre-compact.jsto capture custom state using utilities fromscripts/lib/utils.js - Verify execution through log files, session annotations, and Claude's stderr output
Frequently Asked Questions
What triggers the Pre‑Compact hook to run?
Claude‑Code invokes the Pre‑Compact hook automatically when a conversation approaches the configured context‑window limit and the system prepares to summarize older messages. This occurs transparently during long sessions without user intervention. The hook fires once per compaction event.
Can I disable the Pre‑Compact hook if I don't need it?
Yes. Remove or comment out the PreCompact array in hooks/hooks.json (lines 56-66). Claude‑Code will then compact context without executing custom preservation logic. Alternatively, set the matcher to a non‑matching pattern like "none" to disable it selectively.
How is this different from the Post‑Compact hook?
The Pre‑Compact hook runs before summarization, giving you access to the full, uncompacted conversation state. A Post‑Compact hook would run after the summary is applied, when original messages may already be truncated. Use Pre‑Compact to capture data; use Post‑Compact (if implemented) to react to the new compacted state.
Why does the hook write to stderr instead of stdout?
Claude‑Code captures stderr for log visibility in the interface while treating stdout as potential command output. The log() function in utils.js deliberately writes to process.stderr so compaction notifications appear in Claude's activity panel without interfering with any structured data the hook might emit to stdout.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →