CacheWarm v0.5.2

FAQ

Straight answers about a very small plugin.

What it does, what it refuses to do, what a ping costs, and what it changes in your conversation. Everything here is checked against the shipped code and the project's own verified behaviour notes.

01 Basics

What it is, and when it acts

What does Cache Warm actually do?

It keeps the prompt cache of idle Claude Code sessions warm. At every turn end, a Stop hook starts a small background process — a “waker” — for that session. The waker sleeps until one interval after the conversation's last request. If the session still qualifies for warming, it exits with code 2, which makes Claude Code wake the idle session with a one-line note. Claude replies ok. That single turn reads the whole conversation from the cache and resets the cache timer.

It is not a daemon, a scheduled task, or a monitor process. There is one waker per session and it lives only between turns.

Which Claude Code surfaces does it work in?

The CLI, the Claude Desktop app, and IDE extensions. The plugin is an ordinary hook and runs wherever Claude Code runs hooks. The session's surface is taken from CLAUDE_CODE_ENTRYPOINT, so CLI, Desktop and IDE sessions can be configured independently.

When does it ping by default?

Only when all of these are true: warming is on for that session; something is pending in the background (default warmWhen: background-work); the conversation has sent no request for a full interval; the cache has not expired; activity happened within the idle cap; the context is at least 20,000 tokens; and it has not hit the ping cap.

If you want any idle session warmed, set ccw when always — optionally scoped to a project, surface or session. That costs more, because every idle session in scope is pinged each interval until the idle cap.

Does it run on a fixed schedule while I work?

No. The timer counts from the conversation's last request, and any new request — you typing, a background agent reporting back, a scheduled task firing — supersedes the sleeping waker. The next turn end starts a fresh one. A session in use is never pinged, and nothing fires mid-turn. The project explicitly removed the older cron and monitor engines for this reason.

How do I get the 1-hour cache TTL?

Claude subscriptions get a 1-hour cache within plan limits. With an API key, set CLAUDE_CODE_PROMPT_CACHE_TTL=1h (or the equivalent prompt-cache TTL setting) in Claude Code. The plugin reads the TTL the session is actually using from its transcript, and auto picks a 50-minute interval for a 1-hour cache and a 4-minute interval for a 5-minute cache.

A 1-hour TTL turns roughly fifteen 5-minute pings into one ping per hour, which is why the README recommends it before tuning anything else.

02 Safety

Your cache and your conversation

Can it invalidate my prompt cache?

Not by construction. The cache matches the start of each request. A ping is appended at the end of the conversation like any other turn, so everything before it still matches. The plugin ships no MCP server and changes no tools, system prompt, model or effort level. Claude Code's prompt-caching documentation lists plugin hooks and skills among the components that keep the cache.

Things outside its reach can still invalidate a cache: switching models, /compact, MCP tools changing, or upgrading Claude Code. The dashboard distinguishes verified pings from misses for exactly this reason.

What does it add to my conversation?

Each ping adds two short messages: a “Stop hook feedback” note carrying the keep-alive line, and Claude's ok. The note is how Claude Code surfaces a background hook waking the session. A ping is a real model turn — one cache read of the full context plus a few output tokens.

Will a ping interrupt me?

No. A waker only exists between turns. The moment a turn starts it stands down, and a session that is busy is never due for a ping. The plugin also does not use decision: "block" Stop hooks or scheduled tasks.

Can one session change another one's behaviour?

No. Each session has its own waker and resolves its own settings. A Desktop session cannot change a CLI session by accident. Inside a Claude Code session, an unscoped ccw change applies only to that session's surface; --global is required to reach every surface. Every read-modify-write of shared state takes a cross-process lock and writes atomically.

Does it send my data anywhere?

The plugin itself makes no network requests. The ping is an ordinary Claude Code turn and goes wherever your Claude Code traffic already goes. The dashboard binds to 127.0.0.1 only and computes analytics read-only from local transcripts. State lives in ~/.claude-cache-warm/ (override with CCW_HOME). The dashboard's write endpoints require a loopback Host, a same-origin Origin and a JSON body.

See Security & architecture for the file and API inventory.

What happens if I uninstall it?

The status line is restored exactly when you turn the segment off (ccw statusline off) or uninstall it. If the plugin is removed some other way, the status line launcher falls back to your original command. State files under ~/.claude-cache-warm/ are left alone; delete that folder to start fresh. Unrestarted sessions keep the hooks they loaded until /reload-plugins or a restart.

03 Cost

What a ping buys

What does one keep-alive ping cost?

One cache read of the whole context, plus a few output tokens. On list prices a cache read is 0.1× the base input price (0.025× for Fable and Mythos 5.1). For an 800K-token context on Opus 5 with a 1-hour cache, the plugin's estimator reports about $0.40 per ping. A cold return of the same context is about $8.00 — a cache write at 2× input price.

These are estimates from list prices, not a bill.

Will it save me money?

Only if you actually return to large sessions after long pauses. One rebuild buys roughly 18 pings in the example above. Pings stop paying for themselves once they cost more than one rebuild: the idle cap and the hard per-stretch ping cap bound the downside.

Check the dashboard's “Recent cold rebuilds” table first. It shows what your real pauses cost, what warming would have cost instead, and a verdict for each one.

Does it count against my subscription usage limits?

Yes. A ping is a real model request. On a subscription it counts against your usage limits; on an API key it is billed. That is why the default is conservative: background work only, and skipped when the cache is already cold or the context is small.

How do I make it cheaper?
  • Get the 1-hour TTL first: it turns many pings into one per hour.
  • Keep the default warmWhen: background-work; avoid always on large scopes.
  • Lower maxIdleMinutes so forgotten sessions stop sooner.
  • Raise minContextTokens so small contexts are skipped.
  • Use the dashboard to check real rebuilds before enabling a scope.

04 Troubleshooting

Why isn't it pinging?

My session says “standing by” or “no-work”.

That is the default policy working: nothing is pending in the background for that session. Use ccw when always — optionally --session or --project — to warm a plain idle session. Remember that monitors are watchers that can run forever and deliberately do not count as pending work.

My session is not listed at all.

A session registers on its first hook event after the plugin is enabled. Run /reload-plugins in sessions that were already open, or start a new one. Headless runs (claude -p, Agent SDK) are never registered, by design.

It says “expired” after my laptop slept.

The cache timed out while the machine slept. The plugin will not ping an expired cache, because that ping would be the expensive rewrite. The next real turn rebuilds it normally.

I see “interval … is not shorter than the … TTL”.

That session is using the 5-minute cache, and the configured interval is 5 minutes or longer. Use ccw interval auto, or enable the 1-hour TTL. auto reads the TTL from the session's transcript and picks 4 or 50 minutes.

Some pings are not verified as cache hits. Is that a bug?

Not necessarily. Something else can invalidate the cache between the ping and the next request: a model switch, /compact, MCP tools changing, or an upgrade. Two verified misses in a row suspend warming for that session until you are active in it again, so a broken setup cannot keep paying for rewrites. ccw doctor shows verified pings per surface; ccw events 30 shows the recent log.

How do I prove the whole loop works?

Run /cache-warm:test (or ccw test) in any session, CLI or Desktop app, then do not type for about a minute. A keep-alive note arrives, Claude answers ok, the ping is checked against the transcript, and the session's prior settings are restored. If nothing arrives within two minutes, that surface is not waking sessions; settings restore themselves after fifteen minutes.

The status line segment is missing in one folder.

That folder has its own status line, which wins over the user-level one. Run ccw statusline there to see which file is in effect, then ccw statusline install --file=<that file>. A shared project status line is never edited; the plugin uses a personal settings.local.json overlay instead.

05 Limits

What it cannot do

Does it keep a foreground subagent's cache warm?

No. A foreground subagent blocks the main conversation inside a tool call; nothing can refresh the main cache until it returns. Warming covers background work — subagents, background tasks, background shells, workflows and scheduled tasks that the session is waiting on.

Can it stop other things from invalidating the cache?

No. A model switch, /compact, a change in MCP tools, or a Claude Code upgrade can invalidate the context regardless of warming. See How Claude Code uses prompt caching.

Which platforms are supported?

The plugin, CLI, dashboard and status line are Node.js (18+) and platform-neutral. The optional tray app is Tauri v2; the repository's development notes and packaging instructions are written for Windows (MSVC toolchain, WebView2). It is optional — every feature except the global hotkeys is available without it.

I'm upgrading from an older version. What happens to my state?

On the first hook run after updating, the state directory is migrated automatically. A backup is written to config.v1.backup.json; values that only mirrored the plugin tab go back to the plugin tab; per-session switches become session overrides; ping history is kept; junk registrations are pruned. Leftover 0.2 scheduled tasks are told to delete themselves when they next fire. Run /reload-plugins in every open session after updating.

Still stuck? ccw doctor and ccw events 30 answer most questions. Open an issue on GitHub.