Moved: now maintained at https://github.com/steven4354/claude-idle-reaper — with a one-line installer:
curl -fsSL https://raw.githubusercontent.com/steven4354/claude-idle-reaper/main/install.sh | bash
Free the RAM held by idle Claude Code sessions — without losing them.
Every interactive Claude Code session holds 200–600+ MB of RAM forever, even
when it's been sitting idle in a terminal tab for days. If you work with many
tabs (Warp, iTerm, tmux), this quietly eats 10–15 GB and fills your swap. There
is no built-in idle-timeout, session-suspend, or memory-limit setting, and the
NODE_OPTIONS --max-old-space-size workaround doesn't apply to the native
binary install. (See anthropics/claude-code issues
#25545,
#18859,
#4953.)
The key insight — borrowed from how OpenAI's Codex CLI stays lean — is that
an idle session has no reason to exist as a process. Claude Code persists
every transcript to disk continuously, and claude --resume <id> restores a
session losslessly. So: kill idle sessions, keep the resume path warm.
A ~130-line bash script, run hourly by launchd, that:
- Finds
claudeTUI processes that are safely idle — all of:- no keystroke in their tab for
IDLE_HOURS(default 4h, via tty atime) - CPU at 0
- no new transcript message for
QUIET_MINS(default 2h) — this protects autonomous agents/loops that work without keyboard input - confidently mapped to their session transcript (unmappable → never killed)
- no keystroke in their tab for
- SIGTERMs them (graceful; Claude Code exits cleanly)
- Summarizes the session transcript with
claude --model haiku -p - Prints the summary into the dead session's own tab, so instead of a blank screen you see:
────────────────────────────────────────────
💤 Idle Claude session closed to free 334MB (no input for 21h)
Topic: Diagnosing task system failures via telemetry
- Queried settlement telemetry and prod DB; found 22 tasks across 5 failure classes
- Judge-starvation P0 still live; no fix PR on main yet
- Applied debugging frameworks to map cascading mitigations as root causes
Pending: Write P0 fix PR, merge circuit breaker, add staleness alert.
▶ Pick up where you left off: claude --resume 10da943d-4991-4ec7-a4a1-46287e89d3a1
────────────────────────────────────────────
(Without this, the tab goes blank on exit — Claude Code renders on the terminal's alternate screen, so the conversation vanishes with the process.)
mkdir -p ~/.claude/scripts
cp reap-idle-claude.sh ~/.claude/scripts/
chmod +x ~/.claude/scripts/reap-idle-claude.sh
sed "s|/Users/USERNAME|$HOME|g" com.user.claude-idle-reaper.plist \
> ~/Library/LaunchAgents/com.user.claude-idle-reaper.plist
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.user.claude-idle-reaper.plistTry it first without killing anything:
DRY_RUN=1 bash ~/.claude/scripts/reap-idle-claude.shReap one specific session on demand (overriding the idle guards):
ONLY_PID=12345 IDLE_HOURS=0 QUIET_MINS=0 bash ~/.claude/scripts/reap-idle-claude.shUninstall:
launchctl bootout gui/$(id -u)/com.user.claude-idle-reaper
rm ~/Library/LaunchAgents/com.user.claude-idle-reaper.plistLog: ~/.claude/scripts/idle-reaper.log
- PID → session mapping without lsof. Claude Code doesn't keep its
transcript file open. Resumed sessions carry the session UUID in their
command line (
claude --resume <uuid>); fresh sessions are matched by transcript file birth time (stat -f %B) falling just after process start. Near-simultaneous launches are disambiguated by claiming each transcript at most once; anything ambiguous is skipped, not killed. - Transcript mtime lies. Claude Code bulk-touches transcript mtimes
(dozens of files at the same second, e.g. during startup grooming), so
"recently modified" ≠ "recently active". The script reads the last embedded
"timestamp":from the file tail instead. - Idle = tty atime. Last keyboard input in the tab is
stat -f %a /dev/ttysNNN— the same signalwuses. - The summary is printed after the process dies, so it lands on the normal screen buffer (the alternate screen is gone by then) and persists in the tab.
- macOS only (BSD
stat/date/launchd). Linux port would needstat -c, GNUdate -d, and a systemd user timer. - A session that's mid-turn but blocked on a very long API call shows 0% CPU; the 4h keyboard-idle + 2h transcript-quiet thresholds are what protect it. Don't crank both to near zero in the plist.
- The Haiku summary costs one small API call per reaped session.