Skip to content

Repository files navigation

unforget

Re-inject the working state Claude Code loses when it compacts context.

npm version npm downloads CI License: MIT Zero dependencies

unforget recovering working state after a compaction

When Claude Code compacts a long session it swaps the transcript for a short summary, and that summary is lossy: the file you were mid-edit on, the command that just failed, the approach you ruled out, the constraint you set 40 turns ago — gone.

unforget reads the on-disk transcript (append-only, survives compaction), works out exactly what was dropped, and re-injects a compact digest of it through a SessionStart hook — so the fresh context picks up where you left off.

  • Zero runtime dependencies. Local only, never a network call.
  • Reads only ~/.claude (or $CLAUDE_CONFIG_DIR); writes nothing but its own hook config.
  • Silent when there's nothing to recover — safe to wire to a hook.

Install

npm install -g unforget   # or: bunx unforget / npx unforget

Use

Add the hook once; every post-compaction session then starts with the digest re-injected:

unforget init           # shows the settings.json change, asks, backs up, then writes
unforget init --remove  # uninstall
unforget init --print   # print the hooks block instead of writing

It registers a SessionStart hook (matcher compact) with absolute paths, so it still fires when Claude Code launches from a GUI app with a minimal PATH. Symlink-safe; refuses a settings file it can't parse.

Inspect or verify by hand:

unforget digest [path]   # what the newest compaction dropped (--boundary N, --json)
unforget doctor    # hook installed? paths on disk? last injection fresh?

Does it work?

The built-in summary already covers most mechanical rediscovery; unforget catches what it drops. In the author's own dogfooding — 135 compaction boundaries, 62 of them post-install, self-measured:

  • 98% of the working state the summary dropped was carried by the digest (22.7% of all rediscovery events, most of which the summary already covered).
  • 3.15 → 1.97 average rediscoveries per compaction, pre- vs post-install (files re-read / commands re-run that were already known). The pre-install half is correlational — different sessions, not a controlled A/B.
  • ~854 tokens of that state re-injected per compaction that needed it.

Single-machine sample — directional, not a guarantee. Run bun bench/retro.ts against your own ~/.claude to measure it yourself.

How it works

  1. Find the newest transcript's last compact_boundary; read which message UUIDs survived.
  2. Dropped set = messages since the previous boundary that weren't preserved.
  3. Extract a deterministic digest from the dropped records: active task, in-flight edits, failed commands + last test result, dead-ends, constraints (don't / always …), next step.
  4. Drop anything the built-in summary already carries (summary read as a filter, never a source).
  5. Render markdown, empty sections omitted, capped at 9,500 characters.

Claude Code flushes the new boundary to disk only after hooks run, so the live hook (inject) windows from the last flushed boundary to end-of-file — the just-compacted turns — while digest reconstructs it after the fact. See docs/hook-contract.md.

Privacy

Reconstructing working state means reading message content — but only under your own ~/.claude, writing nothing outside its own config, with no network calls. Nothing leaves your machine.

Development

bun test && bunx tsc --noEmit && biome check src test && bun run build

MIT

About

Re-inject the working state Claude Code loses when it compacts context — a local, zero-dependency SessionStart hook

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages