⏳ This skill is pending AI review.
Scores will appear once the review pipeline completes.
cachebeat
Keep the Anthropic prompt cache warm by firing a tiny heartbeat only after N minutes of true session inactivity (any exchange resets the clock), so the next real turn reads the conversation at cached price / cached quota instead of a full uncached re-read. The heartbeat is a background Stop hook this skill registers (asyncRewake) - it re-arms itself after every turn, with no tool calls and no permission prompts. Use when the session may sit idle (training runs, CI, downloads, the user stepping away) longer than the cache TTL. Args - idle threshold in minutes (default 50), optional max hours (default 8), or "stop".
Choose how to use this skill
You do not need every option. Choose the path your AI client supports. The stable page stays the same; versioned files are immutable.
1. Native installer
This listing has no registered native installer command. Use the complete package or source fallback below, depending on what your client supports.
Do not guess an installer command or replace an existing version without reviewing the diff.
2. Complete package recommended
Download the ZIP when available. It includes SKILL.md plus the references, security notes and version metadata.
No complete ProSkills package is published for this listing yet.3. Prompt-only
Copy the prompt above when the agent can read the stable page or when you want to adopt the workflow without installing a skill.
Need only the instruction file?
Download SKILL.md only if your client requires a single file. The complete ZIP is safer for a full installation because it preserves the references and release context.
No path installs or executes anything by itself. Your agent still needs access to the project files. Before updating, compare the installed version and review the diff.
// RATINGS
// README
cachebeat 🫀
Stop paying full price to say "I'm back" to your own Claude Code session.
A tiny Claude Code skill that keeps your prompt cache warm while a session sits idle — so your next message reads the conversation from cache instead of re-billing it from scratch. Anthropic bills cache reads at 0.1× the input rate (published ratio, every model) — an uncached return costs ~10× more.
This is not just an API-billing thing. On Pro/Max subscriptions the same accounting drains your 5-hour window and weekly usage limit: come back to a long session cold, and one "how's it going?" can eat a visible chunk of your quota that a warm cache would barely have touched.
Why this exists
It was born in a real session: a multi-day model fine-tune babysat by Claude — training runs that took hours, checkpoints landing overnight, a human who occasionally sleeps. Every time the session went quiet for longer than the cache window, the next "how's it going?" re-billed the entire conversation history at full input price. On a long, tool-heavy session that's hundreds of thousands of tokens, re-billed every single time you stepped away too long.
One inactivity-triggered heartbeat later, it didn't.
The problem, concretely
Claude is stateless: your whole conversation is re-sent to the API on every turn. Anthropic's prompt caching makes this affordable — cached input costs ~10 % of the normal rate — but the cache expires after a TTL (up to 1 hour on long-TTL sessions). The failure mode:
- You run something long (training, CI, a big download) or just step away.
- The session is silent past the TTL. The cache dies.
- You come back, type one line — and the entire history is re-processed uncached: ~10× the tokens a cached read would have counted. On the API that's money; on a subscription that's your 5-hour window and weekly limit draining for nothing.
Do that a few times a day on a long-running session and the "idle tax" quietly becomes the biggest line in your bill — or the reason you hit your usage limit by mid-week.
The fix
/cachebeat arms a silent background watcher inside your session. It's an inactivity timer, not a
metronome: it fires only when the session has been truly silent for N minutes — any message, reply,
or answered background event already refreshed the cache and resets the clock. On firing, Claude
wakes, answers with a single ., and the cache TTL restarts. The next timer starts by itself.
Break-even math: going cold once costs as much as ten heartbeats. Come back to the session even once and the heartbeat has paid for itself many times over — and the bigger the session, the bigger the absolute savings (the 10× ratio is flat; the token count isn't).
It also kills itself after a deadline (default 8 h), so an abandoned session doesn't drip-bill forever.
Note (Oct 2026): Claude Code 2.1.285 put a lifetime on background shell commands (30 min by default, 2 h at most) and tells Claude not to restart one that hit the 2 h cap. The previous cachebeat ran as exactly such a command, so it was killed before its first 50-minute beat (or at 2 h on a busy session) and never came back (#1). cachebeat now runs as an
asyncRewakeStop hook declared in the skill itself: no lifetime cap, no re-arm tool call, no permission prompt, and one request per beat instead of two. Upgrading? Copy both files again (see Install), then run/cachebeatonce in each session you want covered. A session still running the old instructions stops at its next re-arm instead of looping.
Install
User-level (all projects):
mkdir -p ~/.claude/skills/cachebeat
cp SKILL.md beat.sh ~/.claude/skills/cachebeat/
Or project-level: copy both files into <your-project>/.claude/skills/cachebeat/.
That's the whole setup — two small files: the skill, and the watcher script its hook runs. Then in any
Claude Code session, type /cachebeat and you're done. There is nothing to approve: hooks don't ask
for permission, and Claude never runs a command for it.
Requirements: a recent Claude Code (tested on 2.1.288) with hooks enabled — skill-frontmatter hooks and
asyncRewake are standard features, but an organization policy that disables hooks
(disableAllHooks, allowManagedHooksOnly) also disables cachebeat.
Usage
/cachebeat # fire after 50 min of inactivity, auto-stop after 8 h
/cachebeat 40 # custom idle threshold in minutes (clamped to 5–55)
/cachebeat 40 4 # custom threshold + auto-stop after 4 h
/cachebeat stop # stop it
You can also just ask Claude ("keep the cache warm while this trains") — it invokes the skill for you.
Verifying it's working
You don't have to take it on faith — you can watch the cache stay warm.
-
The heartbeat itself. When a beat fires you'll see a
⏺ cachebeat heartbeatline and Claude answering.— proof the timer is alive and resetting the TTL. If instead Claude tells you cachebeat stopped itself, the beat fired on schedule but the cache had still expired: your session's TTL is too short for any heartbeat to bridge. -
The status line. Claude Code's token readout looks like
tok:312.0k/0.0k— total / served from cache. When you return to an idle session, the second number should be a large fraction of the first (warm). If it reads…/0.0k, the whole context was re-read uncached — the cache had died. With cachebeat armed on a long-TTL session, it should stay warm across your idle gaps. -
The transcript (exact numbers). Every assistant reply records what it cost. Run this inside the session (prefix a shell command with
!):F=$(ls ~/.claude/projects/*/"$CLAUDE_CODE_SESSION_ID".jsonl 2>/dev/null | head -1) echo "cache_read: $(tail -n 400 "$F" | grep -oE '"cache_read_input_tokens":[0-9]+' | tail -1 | grep -oE '[0-9]+')" echo "cache_creation: $(tail -n 400 "$F" | grep -oE '"cache_creation_input_tokens":[0-9]+' | tail -1 | grep -oE '[0-9]+')"A warm turn shows a big
cache_readand a tinycache_creation. A cold turn is the reverse — a largecache_creationmeans the context was rebuilt from scratch at full price.
How it works
The skill's frontmatter declares a Stop hook with asyncRewake: true. Claude Code registers it when
you invoke /cachebeat, and from then on runs beat.sh in the background at the end of
every turn. Each run is one inactivity timer. If the hook exits with code 2, Claude Code wakes Claude
and shows it what the hook printed:
- A turn ends → the Stop hook starts a timer (and any older timer notices and exits).
- Every 30 s the timer checks when the last real model response was written to the session
transcript (
~/.claude/projects/<slug>/<session-id>.jsonl). A new response resets the clock. - After N idle minutes it prints a one-line heartbeat note and exits 2 → Claude wakes, reads the
conversation from cache (that read is the heartbeat), and answers
.. - That reply ends a turn → step 1. Claude never re-arms anything.
The heart of it:
while :; do
sleep 30
[ "$(cat "$S.pid")" = $$ ] || exit 0 # a newer turn started a newer timer
SIG=$(sig "$(last_resp)") # the last real model response
[ "$SIG" != "$PREV" ] && { PREV=$SIG; LAST=$NOW; } # new request -> reset the clock
IDLE=$((NOW - LAST))
[ "$IDLE" -ge $((N*60)) ] && { echo "idle heartbeat …" >&2; exit 2; } # exit 2 wakes Claude
done
| exit | meaning | what Claude does |
|---|---|---|
0 | nothing to say: not armed, stopped, superseded, or the cache is already cold | nothing — no wake-up |
2 + "idle heartbeat" | the session sat idle for the threshold | replies . (the reply's turn starts the next timer) |
2 + "stopped itself" | the last beat *sti |
// HOW IT'S BUILT
KEY FILES