SLOPSHOPPER

cache-band

A line above the prompt: how many tokens the prompt cache holds for this conversation

newband
v0.1.0NOASSERTIONupdated 2026-10-08Tiutiunov/Kisel/mods/cache-band
A shopper browsing a rack in a slop shop
README

<img src="docs/img/hero.png" alt="Kisel" width="900">

<a href="https://github.com/Tiutiunov/Kisel/releases/latest"><img src="https://img.shields.io/github/v/release/Tiutiunov/Kisel?label=%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D1%8F&color=39C5BB" alt="Последняя версия"></a> <img src="https://img.shields.io/badge/Windows-10%20%2F%2011-E0405A" alt="Windows 10 и 11"> <img src="https://img.shields.io/badge/Qt%206-QML%20%2B%20C%2B%2B20-B9DC6B" alt="Qt 6"> <a href="README.en.md"><img src="https://img.shields.io/badge/README-English-F5A3C0" alt="English"></a>

<h3 align="center"> <a href="https://github.com/Tiutiunov/Kisel/releases/latest">Скачать для Windows</a> </h3>

Kisel это маленькая тёмная таблетка у края экрана, в которой сидят пять моти. Изначально она нужна была для одного: видеть, чем занят Claude Code, не переключаясь в терминал, и отвечать на его «можно я запущу эту команду?» одним кликом. Потом в таблетку переехали плеер, уведомления, монитор железа и замер интернета, и теперь я её просто не закрываю.

Внутри нет ни одной картинки. Персонажи, иконки, искры, даже звуки сделаны кодом.

Кто там сидит

<img src="docs/img/cast.png" alt="Miku, Rin, Luka, Zundamon и Teto" width="900">

Чем занятаКакая она
MikuClaude Code: сессии, разрешения, вопросы, чатСтарательная, напевает под нос. Шлёпнешь, надуется
RinУведомления Windows: машет табличкой с названием программыШумная. От шлепка взрывается
LukaИнтернет: пинг, скорость, загрузки, замерСпокойная. На шлепок смотрит как на идиота
ZundamonМузыка из SpotifyХвастун, но паникует первым
TetoПроцессор, видеокарта, памятьСамодовольная, пока её не похвалишь

Главную выбираешь кликом по скамейке справа. Остальные никуда не деваются: сидят рядом, моргают, иногда корчат рожи.

Кто чем занят, можно переставить в настройках: хоть Тето на Claude Code, хоть Мику на музыку. Характер и любимая еда остаются при персонаже, работа переходит.

Таблетка

288 на 32 пикселя, живёт у любого края экрана. Что в ней показано, зависит от того, что сейчас происходит.

<img src="docs/img/bar.png" alt="Таблетка в разных состояниях" width="900">

Пара вещей, на которые ушло больше времени, чем стоило бы:

  • На скамейку и обратно они летят сальто через всю таблетку. Просто исчезнуть было бы скучно.
  • Пока Claude работает, Мику садится слева с его оранжевым зверьком в лапках, а рядом кто-нибудь составляет ей компанию. Они толкаются, перекидывают звёздочку, играют в прятки, тянутся к зверьку. Сценок больше сорока, и у каждой есть своя: над Тето смеются, и она замахивается метлой.
  • Замер скорости и чистка памяти запускаются прямо из таблетки, и пока они идут, таблетка наполняется жидкостью.
  • Если играет музыка и их хотя бы двое, время от времени начинается танец.
  • Минуту ничего не происходит, и таблетка уезжает за край. Если играет музыка, остаётся мини-плеером, и по её краю можно перематывать трек.
  • В полноэкранной игре она опускается к самому низу экрана, чтобы не висеть посреди картинки.
  • Двигается кнопкой со стрелками в шапке карточки: она отлипает от края, идёт за курсором как желе, а когда отпустишь, сама летит к ближайшему краю.
  • При запуске Мику выныривает снизу экрана, машет, и только потом появляется таблетка, в которую она улетает.

<img src="docs/img/bar-side.png" alt="Таблетка у бокового края" width="900">

Карточки

Клик по главной раскрывает карточку. У каждой своя.

<table> <tr> <td width="50%"><img src="docs/img/card-miku.png" alt="Карточка Мику"><br><b>Miku.</b> Что делает последняя сессия и три кольца лимитов: пять часов, неделя и заполненность контекста. Под ними написано, когда лимит сбросится.</td> <td width="50%"><img src="docs/img/card-permission.png" alt="Запрос разрешения"><br><b>Разрешения.</b> Claude хочет что-то запустить или поправить, и карточка открывается сама. За тебя Kisel не отвечает никогда, только твой клик.</td> </tr> <tr> <td><img src="docs/img/card-question.png" alt="Вопрос от Claude"><br><b>Вопросы.</b> Варианты кнопками, ответ уходит обратно в Claude Code.</td> <td><img src="docs/img/card-done.png" alt="Задача готова"><br><b>Готово.</b> Мику празднует, вокруг летает лук-порей. У каждой своя любимая еда.</td> </tr> <tr> <td><img src="docs/img/card-zunda.png" alt="Плеер"><br><b>Zundamon.</b> Обложка, название, кнопки, перемотка. Берёт данные из системного медиа-сеанса Windows, так что Spotify трогать не надо.</td> <td><img src="docs/img/card-teto.png" alt="Монитор"><br><b>Teto.</b> Нагрузка за последнюю минуту и кнопка, которая чистит память. Сколько освободила, расскажет сама и не без гордости.</td> </tr> <tr> <td colspan="2"><img src="docs/img/card-chat.png" alt="Чат"><br><b>Чат.</b> Пишешь здесь, а уходит это в твою запущенную сессию Claude Code как обычный промпт. Ответы возвращаются сюда же, слева лимиты и модель. Ключ API не нужен.</td> </tr> <tr> <td><img src="docs/img/card-luka.png" alt="Сеть"><br><b>Luka.</b> Пинг и текущая скорость. Кнопка Test меряет канал без захода на speedtest. Чем быстрее интернет, тем она довольнее, на медленном расстраивается.</td> <td><img src="docs/img/card-rin.png" alt="Уведомления"><br><b>Rin.</b> Держит табличку с названием программы, пока не посмотришь. Клик по ней открывает эту программу. Про новую версию Kisel тоже сообщает она.</td> </tr> </table>

<img src="docs/img/card-settings.png" alt="Настройки" width="620">

Настройки разложены по вкладкам: Claude Code, сервисы, персонажи, вид и звук, место, сам Kisel.

Все анимации и сценки можно погонять отдельно: открой в браузере design/miku-editor.html. Там же ползунки для внешности.

Как поставить

Скачай KiselSetup со страницы релизов и запусти. Ставится в профиль пользователя, права администратора не нужны. Нужна Windows 10 (1803+) или 11.

Установщик не подписан, сертификат стоит денег. Поэтому SmartScreen ругнётся: жми «Подробнее», потом «Выполнить в любом случае».

Обновляется сам. Раз в сутки спрашивает GitHub, не вышло ли чего, и если вышло, Рин поднимает табличку с номером версии. Дальше одна кнопка в настройках: скачает, сверит контрольную сумму, поставит и перезапустится. Проверку можно выключить там же.

Удаляется через «Приложения» в параметрах Windows. Хуки из настроек Claude Code при этом убираются, твои настройки остаются.

Как подружить с Claude Code

Карточка → шестерёнка → Connect Claude Code. Kisel покажет, что именно допишет в ~/.claude/settings.json, и не тронет файл, пока не нажмёшь. Перед записью делает копию.

Дальше Claude Code на каждое событие дёргает крошечную kisel-hook, а она передаёт его Киселю. Если Kisel не запущен, хук молча выходит и Claude Code ничего не замечает. Если хуки пропали или устарели после обновления, Kisel скажет об этом и предложит починить.

Моды: чат и лимиты

Там же, в настройках, кнопка Install mods. Она ставит в Claude Code два плагина, которые идут вместе с Kisel:

  • kisel-prompts связывает сессию с чатом Kisel: промпты туда, ответы и лимиты обратно.
  • cache-band показывает над полем ввода в Claude Code, сколько токенов лежит в кеше.

Kisel для этого запускает родные команды claude plugin ... и показывает их заранее, так что нужен установленный claude в командной строке. Моды загружаются в сессиях, запущенных после установки.

Discord

По желанию Kisel показывает в профиле Discord «Работает с Claude Code» с таймером. Выключено, пока не включишь в настройках. Название сессии показывается, только если включить и это. Общается он только с Discord на твоём компьютере.

Про данные

Секреты (токен GitHub для виджета) лежат в диспетчере учётных данных Windows, в файлы не пишутся.

Телеметрии нет. В сеть Kisel ходит за виджетом GitHub, пингует 1.1.1.1 для Луки, раз в сутки проверяет обновления и по кнопке меряет скорость через Cloudflare. Всё это отключается или не запускается само. Чат идёт через твою локальную сессию Claude Code, а не через сеть Kisel.

Подробно, по пунктам: docs/PRIVACY.md. Условия использования: docs/TERMS.md.

Из уведомлений читается только название программы и количество. Текст не читается.

Настройки лежат в %LOCALAPPDATA%\kisel\kisel.conf, обычный текстовый файл.

Собрать самому

Нужны Visual Studio 2022 с C++, CMake, Ninja и Qt 6.5+ для MSVC. Из «x64 Native Tools Command Prompt»:

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=C:\Qt\6.10.3\msvc2022_64
cmake --build build
cmake --install build --prefix dist
dist\bin\kisel.exe

Установщик: cmake --build build --target installer.

Без Claude Code можно посмотреть так: kisel.exe --demo разыграет выдуманную сессию. kisel.exe --speed-test просто померит интернет и напечатает результат. Как всё устроено внутри, написано в docs/ARCHITECTURE.md.

Откуда взялся

Kisel начал Антон Куклин как спутник для KDE Plasma. Эта ветка — версия для Windows, которая с тех пор обросла всем остальным. Форму персонажей подсмотрели у Mochi из Coucou.

Лицензии и права

Код под MIT. Это касается только кода.

Персонажи не мои. Kisel это фанатская некоммерческая штука, с правообладателями она не связана и ими не одобрена. Нарисовано всё своим кодом и в своём стиле, чужих иллюстраций, голосов и музыки в проекте нет.

この作品はピアプロ・キャラクター・ライセンスに基づいてクリプトン・フューチャー・メディア株式会社のキャラクター「初音ミク」「鏡音リン」「巡音ルカ」を描いたものです。

Kisel не имеет отношения к Anthropic. Claude и Claude Code это их товарные знаки, и маленький оранжевый зверёк, которого Мику обнимает за работой, тоже их персонаж из Claude Code.

Внутри Qt 6 по LGPL v3, подключён динамически: библиотеки лежат рядом с kisel.exe как есть, их можно заменить своей сборкой. Исходники Qt на download.qt.io. Тексты лицензий ставятся в папку licenses, остальное перечислено в THIRD-PARTY-NOTICES.md.

Source 3 files
hooks/register.tsx 61 lines
1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import type { CacheCount } from '../types'
5import { line } from './format'
6
7const last = atom({ plugin: 'cache-band', key: 'last' } as const, null)
8
9export const register: Register = on => {
10  // A session picked up mid-way: what its last response reported, if the engine still has it.
11  on('session.start', async ($, e, next) => {
12    const result = await next(e)
13    const usage = (await $.session.usage({ breakdown: 'summary' })).context.breakdown?.apiUsage
14
15    if (usage && (await read($, last)) === null) {
16      const seen: CacheCount = {
17        read: usage.cache_read_input_tokens,
18        written: usage.cache_creation_input_tokens,
19        fresh: usage.input_tokens,
20      }
21      await update($, last, () => seen)
22    }
23
24    return result
25  })
26
27  // Every model response of the main conversation (a subagent has a cache of its own).
28  on('turn.step', async function* ($, e, next) {
29    const result = yield* next(e)
30
31    if (e.agentId === undefined && result.usage) {
32      const seen: CacheCount = {
33        read: result.usage.cache_read_input_tokens,
34        written: result.usage.cache_creation_input_tokens,
35        fresh: result.usage.input_tokens,
36      }
37      await update($, last, () => seen)
38    }
39
40    return result
41  })
42
43  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
44    const seen = await read($, last)
45
46    if (e.props.hasSurvey || seen === null) {
47      return next(e)
48    }
49
50    const { Box, Text } = $.ui.resolve(e)
51
52    return (
53      <Box>
54        <Text key="cache" dimColor>
55          {line(seen)}
56        </Text>
57      </Box>
58    )
59  })
60}
61
hooks/format.ts 14 lines
1// 182431 -> "182.4k", 950 -> "950", 1234567 -> "1.23M"
2export const short = (n: number): string =>
3  n >= 1_000_000 ? `${(n / 1_000_000).toFixed(2)}M` : n >= 1000 ? `${(n / 1000).toFixed(1)}k` : `${n}`
4
5// What the band says. In the cache after a response: what it read from the
6// cache plus what it added to it.
7export const line = (c: { read: number; written: number; fresh: number }): string => {
8  const held = c.read + c.written
9  const all = held + c.fresh
10  const share = all > 0 ? Math.round((c.read / all) * 100) : 0
11
12  return `В кеше ${short(held)} токенов · из кеша ${short(c.read)} (${share}%) · дописано ${short(c.written)} · мимо кеша ${short(c.fresh)}`
13}
14
types/index.d.ts 16 lines
1// What the last response of the main conversation said about the prompt cache.
2export type CacheCount = {
3  /** input tokens the cache served */
4  read: number
5  /** input tokens the response wrote to the cache */
6  written: number
7  /** input tokens that went past the cache */
8  fresh: number
9}
10
11declare module 'claude-code' {
12  interface PluginState {
13    'cache-band': { last: CacheCount | null }
14  }
15}
16