#!/usr/bin/env node // Hook: post-compact-flush.mjs // Event: PostCompact (Claude Code v2.1.105+) // Purpose: Re-inject .session-state.local.json after compaction so // /trekcontinue and `/trekexecute --resume` see fresh // session-state and the model has Handover 7 context immediately // after a context-compaction event. // // Read-only — never writes. Always exits 0; never blocks compaction. // // Behavior: // 1. Auto-discover the most-recently-modified // /.claude/projects/*/.session-state.local.json // 2. Validate it via lib/validators/session-state-validator.mjs // 3. Emit additionalContext containing project + next_session_label + // status so the next assistant turn has resume context loaded. // // Notes: // - Uses only node:fs sync APIs that have existed since Node 12 (no // glob dependency — that requires Node 22). // - Silent no-op if no state file is discoverable, or if the file is // malformed. Compaction must not be blocked under any circumstance. import { readdirSync, statSync } from 'node:fs'; import { join } from 'node:path'; import { validateSessionState } from '../../lib/validators/session-state-validator.mjs'; function findActiveStateFile() { // Auto-discover: most recently modified .session-state.local.json // under /.claude/projects/*/. Returns absolute path or null. const projectsDir = '.claude/projects'; let entries; try { entries = readdirSync(projectsDir, { withFileTypes: true }); } catch { return null; } // .claude/projects/ absent → silent no-op let best = null; let bestMtime = 0; for (const ent of entries) { if (!ent.isDirectory()) continue; const candidate = join(projectsDir, ent.name, '.session-state.local.json'); let st; try { st = statSync(candidate); } catch { continue; } // file missing in this project — skip if (st.mtimeMs > bestMtime) { bestMtime = st.mtimeMs; best = candidate; } } return best; } function main() { const stateFile = findActiveStateFile(); if (!stateFile) { process.stdout.write(JSON.stringify({})); // silent no-op return; } const result = validateSessionState(stateFile); if (!result.valid || !result.parsed) { process.stdout.write(JSON.stringify({})); // silent fail return; } const p = result.parsed; const summary = `[Session resumed after compact] project: ${p.project} next_session: ${p.next_session_label} status: ${p.status}`; process.stdout.write(JSON.stringify({ additionalContext: summary.slice(0, 10000), })); } try { main(); } catch { process.stdout.write(JSON.stringify({})); } // never block compaction process.exit(0);