ClaudeMods
☰
KO
● 0 명 접속 중 · 조회 0 회
후원프로젝트 제출
GitHub 저장소 · 작성자 davidho27941

cache-keeper

키보드에서 떨어져 있는 동안 프롬프트 캐시가 만료되는 것을 방지합니다.

davidho27941@davidho27941

davidho27941/cockpit/tree/master/plugins/cache-keeper

번역 완료

이 mod 소개

Claude Code가 API로 보내는 모든 요청에는 시스템 프롬프트, 도구 정의, 전체 대화와 같은 긴 접두사가 포함됩니다. 이 접두사는 프롬프트 캐시에 기록되며, 이후에 캐시를 사용하는 요청은 비용의 일부만 지불하고 훨씬 빠르게 응답을 받습니다. 그러나 항목은 1시간 동안만 유지됩니다. 마지막으로 사용된 후 1시간이 지나면 사라지고, 다음 차례에는 전체 컨텍스트를 다시 작성해야 하므로 느리고 전체 비용이 발생합니다.

cache-keeper는 세션이 유휴 상태가 될 때까지 기다린 다음, 주기적으로 (기본값 50분) 동일한 대화 접두사를 통해 "reply ok" 한 줄을 보냅니다. 캐시는 다시 사용되고 수명은 1시간 더 연장됩니다.

명령: /cache-keeper (상태), /cache-keeper now (즉시 확인), /cache-keeper off / /cache-keeper on. 설정: interval_minutes (기본값 50), max_idle_hours (기본값 4), enabled, show_band, language. 이 모드는 $.model.fork를 호출하여 주 루프의 마지막 요청을 짧은 사용자 메시지를 추가하여 다시 보내고, 캐시에서 제공합니다. 파일은 읽거나 쓰지 않으며, 명령을 실행하지 않고, 세션 자체의 API 클라이언트를 통한 단일 모델 요청 외에는 네트워크 연결을 열지 않습니다.

설치

먼저 작성자의 README에서 marketplace와 플러그인 이름을 확인하세요. 저장소 구조에 따라 명령어가 달라질 수 있습니다.

claude plugin marketplace add davidho27941/cockpit
claude plugin install cache-keeper
원문 / README

cache-keeper

Keeps the prompt cache from lapsing while you are away from the keyboard.

Every request Claude Code sends to the API carries a long prefix: the system prompt, the tool definitions, the whole conversation. That prefix is written to the prompt cache, and later requests that hit it pay a fraction of the price and come back much faster. But the entry lives one hour: an hour after it was last used it is gone, and the next turn has to rewrite the entire context, slowly and at full price.

cache-keeper waits for the session to idle, then every so often (default 50 minutes) sends one line, "reply ok", over the same conversation prefix. The cache gets used once more and its lifetime extends by another hour.

♨ cache warm · next in 38m · 3 pokes · last hit 98k tok

Usage

Install it and it runs; nothing to configure. The countdown starts when the first turn ends. It never pokes during a turn, and every real request restarts the countdown from the moment the request went out (the cache lifetime counts from the request's start, not from the end of the reply).

| Command | What it does | |---|---| | /cache-keeper | Status: interval, time to the next poke, pokes so far, last cache hit size | | /cache-keeper now | Poke immediately (refused while a turn runs, since that turn refreshes the cache itself) | | /cache-keeper off | Pause warming for this session | | /cache-keeper on | Resume |

Settings

Change them on the /plugin settings page or under pluginConfigs in ~/.claude/settings.json:

| Field | Default | Meaning | |---|---|---| | interval_minutes | 50 | How long the session may idle before a poke. The cache lapses after an hour, so this must be under 60; the mod clamps it to 5–59, leaving ten minutes for clock drift and API latency | | max_idle_hours | 4 | Stop warming once this long has passed since the last real turn. 0 means never stop (see the cost section) | | enabled | true | Off means the mod sends nothing at all | | show_band | true | Draw the countdown line above the prompt | | language | auto | UI language: auto reads LC_ALL, LC_MESSAGES, then LANG (zh* → Traditional Chinese, ja* → Japanese, anything else → English); or set en, zh-TW or ja explicitly |

How it works, and what it costs

Mechanism. The mod calls $.model.fork, which re-sends the main loop's last request exactly (same model, system prompt, tools and conversation) with one very short user message appended and no tools enabled. Because the prefix is byte-identical, the API serves it from the cache and resets the entry's one-hour timer. The reply's cache_read_input_tokens says how much was served; near zero means we were late, the entry had already lapsed, and this request rewrote it.

What one poke costs. The whole prefix at cache-read price, plus a few output tokens. The bigger the context, the dearer the poke.

Price ratios (Anthropic list prices, the model's base input price = 1):

| Item | Multiplier | |---|---| | Cache read (a hit) | 0.1× on most models; 0.05× on Claude Opus 5.5 ($0.20/MTok); 0.025× on Claude Fable 5.1 ($0.25/MTok) | | Cache write, 5-minute TTL | 1.25× | | Cache write, 1-hour TTL (what Claude Code uses) | 2× |

A hit resets the entry's timer at no extra charge, and the lifetime counts from the start of that request.

Worked example. A 100k-token context, a 50-minute interval, Claude Opus 5.5 (input $4/MTok, cache read $0.20/MTok).

  • One poke: 100k × $0.20/MTok ≈ $0.02.
  • Without warming, coming back after more than an hour: the next turn rewrites all 100k at the 1-hour write price, 100k × $8/MTok ≈ $0.80, with a visibly longer time to first token.
  • So: back after about an hour, one poke saves roughly $0.78. Back after four hours, four pokes cost $0.08 and the return saves $0.80, still worth it. Forgot the session overnight, $0.02 an hour burns for nothing. That is why max_idle_hours exists, and why it stops after four hours by default.
  • Other models scale with their ratios: at 0.1× a poke costs a tenth of base input, and a rewrite costs twice base input.

Worth it when the context is large (tens of thousands of tokens or more) and you often step away for ten minutes to an hour (reading, a meeting, waiting on CI). Not worth it when the context is small (there is little to save) or you leave for hours at a time (lower max_idle_hours, or turn it off).

Timeline (50-minute interval, 4-hour cap):

turn ends ─50m─▶ poke ─50m─▶ poke ─50m─▶ poke ─50m─▶ poke ─40m─▶ 4h reached, stop
   any new turn pulls this line back to its start

On failure. An API error (overload, rate limit) backs off: retry after a minute, then two, then four… up to one interval. The first failure shows a toast; later ones only go to the debug log. When there is no reply yet (a new session, or right after /clear) there is nothing to warm and the poke is skipped quietly.

Safety boundary

  • Reads and writes no files, runs no commands, opens no network connection. The one outward action is that single model request, through the session's own API client and credentials.
  • What goes out is the conversation prefix the session has already sent, plus one fixed line. No new information leaves your machine.
  • Only while idle: never during a turn; a running subagent does not affect main's countdown; past the idle cap it stops.
  • At most one poke per interval; a manual /cache-keeper now runs one at a time and never stacks.
  • claude -p and SDK sessions have nobody waiting, so they are never warmed.

What it does before you install it

claude plugin validate ./plugins/cache-keeper

Result (v0.1.0):

hooks: session.start, command.run{command=cache-keeper}, turn.start, turn.step, turn.complete,
       session.end, ui.render{component=AbovePrompt}
calls: $.clock.every, $.clock.now, $.command.register, $.env.get (via resolveLanguage),
       $.model.fork (via poke), $.state.get, $.state.set, $.ui.log (via log), $.ui.resolve,
       $.ui.toast (via toast)
env reads: LANG, LC_ALL, LC_MESSAGES
env writes: nothing
state: cache-keeper.state, cache-keeper.tickAt, cache-keeper.lang
  • $.model.fork: the one poke request; the only thing here that costs money
  • turn.step: read only, to learn that a real request just went out; no chunk or reply is changed
  • $.env.get: the three locale variables, to pick the UI language
  • No $.fs, $.process, $.http

Limits

  • Whether a poke hits is up to the API. Switching models (/model), changing the system prompt, or installing a plugin that changes the tool list all change the prefix, so the next poke is a rewrite and the band's "last hit" drops to near zero.
  • The interval is checked every 30 seconds, so a poke can land up to 30 seconds late.
  • The band is drawn on the terminal and in Claude Desktop's Code tab only; the warming itself runs in any interactive session.
  • Timing state lives in the session's $.state: /clear resets it, a hot reload keeps it, closing Claude Code drops it, and a fresh session counts from its first turn.

Development

claude --plugin-dir ./plugins/cache-keeper   # load once
claude plugin test ./plugins/cache-keeper     # run the tests (27, including the timeline, backoff and languages)

tsconfig.json depends on .claude-plugin/types/, the type declarations Claude Code writes when it loads the mod; they are not committed. UI strings live in hooks/i18n.ts, one dictionary per language, and a test checks that every language has every message.

비슷한 프로젝트