Post a summary to scrap when Claude Code compacts#

When Claude Code compacts a long session, the detail of the earlier part is lost, so a /sc-summarise all written afterwards can only work from the compaction summary. The PreCompact hook in hooks/scrap-precompact.py posts a summary to scrap just before each compaction, while the whole transcript is still there.

Note

Cost and when to skip it. The hook is optional and opt-in: nothing installs it for you. Every compaction waits for it, so expect about 30 s of added delay on a small session (34 s measured on a small test session) and more on a long one, bounded by SCRAP_PRECOMPACT_TIMEOUT (default 180 s, at most 190 s). It also costs one extra model call per compaction. The summary is model-written and can pick up noise from the dialogue, so check the page. If that trade is not worth it, skip the hook, or see Turn it off.

What it does#

  • Runs on the PreCompact event. It reads the session id and transcript_path from the hook input, and turns the transcript into plain dialogue: your messages and Claude’s replies, with tool calls and tool output left out.

  • Runs claude -p headless over that dialogue. It may use only mcp__scrap__tags, search, read and write.

  • Writes one page per Claude session. The first compaction creates it, titled [auto-compact] <topic>, tagged scrap and auto-compact plus topic tags, with the working directory and date as origin. Later compactions of the same session append a section, ## Compaction N (date), covering only what happened since the last one.

  • Keeps a small map of session id to page slug (and how far into the transcript it has summarised) in $XDG_STATE_HOME/scrap/precompact.json (~/.local/state/scrap/precompact.json). Delete the file, or a session’s entry, to make the next compaction start a new page.

  • Never blocks compaction: on any error (no network, scrap down, claude missing, a timeout) it logs to stderr and exits 0. SCRAP_PRECOMPACT_TIMEOUT (seconds, default 180, at most 190) bounds the model call; a value that is not a whole number makes the hook fail at start with exit 1, which Claude Code reports but which still does not block compaction. Compaction waits for the hook, so a slow model delays it by up to that long; keep the hook’s own timeout in settings.json above it.

  • The prompt follows /sc-summarise all (timeline, findings, decisions, changes made, open questions) and tells the model to leave out secrets, tokens, credentials and raw tool output. Check the page rather than trusting that: the model does the redaction.

It costs one extra model call per compaction. If the dialogue exceeds 150,000 characters, the oldest part is left out and the model is told so. If the model writes the page but does not report its slug, the next compaction starts a second page.

Install#

  1. Connect Claude Code to scrap as in Connect a Claude Code session (the claude mcp add line, user scope, so the headless call sees it).

  2. Copy hooks/scrap-precompact.py somewhere stable (for example ~/.claude/hooks/), keeping it executable. It needs only Python 3.

  3. Add the hook to ~/.claude/settings.json (all projects) or .claude/settings.json (one project):

    {
      "hooks": {
        "PreCompact": [
          {
            "hooks": [
              {
                "type": "command",
                "command": "python3 ~/.claude/hooks/scrap-precompact.py",
                "timeout": 200
              }
            ]
          }
        ]
      }
    }
    

The headless call runs with --permission-mode dontAsk, --allowedTools limited to the four scrap tools (tags, search, read, write), --tools "" (no built-in tool at all: no Bash, Edit or web fetch) and --disallowedTools for the scrap tools that edit or trash pages. The last two matter because --allowedTools only approves tools: an allow rule in your own settings.json (say Bash(git:*)) would still apply in the child, and the dialogue it reads can hold text from web pages or files. --tools and --disallowedTools are not widened by your settings. MCP tools of other servers that your settings allow stay available to it; keep broad mcp__* allow rules out of a setup where you do not trust what you read. It does need your Claude Code login and the scrap server to be reachable.

Try it without compacting#

Pipe a hook input made from a real transcript to the script (this does post a page):

t=$(ls -t ~/.claude/projects/*/*.jsonl | head -1)
printf '{"session_id":"test-1","transcript_path":"%s","cwd":"%s"}' "$t" "$PWD" |
  python3 hooks/scrap-precompact.py

Run it twice with more conversation in between to see the second run append. Remove the test-1 entry from the state file afterwards.

Turn it off#

Remove the PreCompact entry from settings.json, or set SCRAP_PRECOMPACT_RUNNING=1 in the environment before Claude Code starts (or in the env block of settings.json). Pages already posted stay; they carry the auto-compact tag (tag:auto-compact), so /sc-tidy can retag or you can delete them in the app.