Единственный читатель этого расширения пишет и думает по-русски. Строки интерфейса, комментарии в коде и сообщения коммитов остаются английскими: их адресат — GNOME Shell и потенциальный сторонний читатель кода, а не владелец репозитория.
9.3 KiB
Claude Code Status
Индикатор для GNOME Shell, отвечающий на один вопрос: какая сессия Claude Code ждёт меня прямо сейчас?
Когда открыто несколько сессий в разных проектах, дорого не «не знать, чем каждая занята», а не заметить, что одна из них встала час назад. Панель называет сессию, а не только состояние.
Состояния
| В панели | Состояние | Что значит |
|---|---|---|
| диск в кольце | blocked |
упёрлась в запрос разрешения — без вас не сдвинется |
| закрашенный диск | waiting |
ход закончен, ждёт вашего ввода |
| кольцо | busy |
работает |
| тусклое пунктирное кольцо | idle |
запущена, но ничего не просили, либо ничего не запущено |
blocked и waiting разведены намеренно. Слитые в одно «требует внимания»,
законченная задача выглядит так же срочно, как заблокированная, — а именно это
различие и решает, переключаться сейчас или после текущей мысли.
Панель показывает самую приоритетную сессию и счётчик +N, если в том же
состоянии есть другие. Внутри состояния выигрывает самая давняя: забывается
та, что ждёт дольше всех, а не последняя.
Уведомлений на рабочий стол нет — сознательно. notify-send из хука было бы
куда дешевле сделать, но форма неверная: сессия, ждущая двадцать минут, должна
оставаться на виду, а уведомление исчезает в момент, когда его смахнули. Панель
здесь — единственный канал.
Установка
git clone <этот репозиторий> ~/projects/private/claude-code-gnome-extension
cd ~/projects/private/claude-code-gnome-extension
# 1. хуки: научить Claude Code публиковать состояние сессий
./hooks/install.py
# 2. расширение: симлинк, компиляция схемы, включение
ln -s "$PWD" ~/.local/share/gnome-shell/extensions/claude-code-status@git.vakhrushev.me
glib-compile-schemas schemas/
gnome-extensions enable claude-code-status@git.vakhrushev.me
Уже запущенные сессии подхватывают хуки без перезапуска — Claude Code перечитывает
settings.json по мере изменения.
На Wayland правки кода расширения по-прежнему требуют релогина; первое включение — нет.
./hooks/install.py --uninstall снимает регистрацию хуков, не трогая остальное в
~/.claude/settings.json. Копия .bak пишется при каждом запуске.
Как это работает
Хуки Claude Code пишут по одному небольшому JSON-файлу на сессию в
~/.local/state/claude-code-status/<session_id>.json; расширение следит за этим
каталогом через Gio.FileMonitor. Ничего не опрашивается, демона нет — смена
состояния доходит до панели, как только хук завершился.
| Хук | Действие |
|---|---|
SessionStart |
сессия появляется как idle, мёртвые подчищаются |
UserPromptSubmit |
busy |
PostToolUse |
busy |
PreCompact |
busy |
Notification |
blocked или waiting, в зависимости от notification_type |
Stop |
waiting |
SessionEnd |
файл удаляется |
PostToolUse не избыточен: это единственное событие, срабатывающее после выдачи
разрешения, — без него сессия остаётся blocked в панели до конца хода. Пишет он
только при реальной смене состояния, так что обычный случай стоит запуска процесса
и нулевого ввода-вывода.
Stop и SessionEnd зарегистрированы синхронно, в отличие от остальных. Оба
срабатывают, когда процесс вот-вот затихнет, и асинхронный хук, проигравший гонку
с выходом, убивается раньше, чем успевает записать: у claude -p это наблюдалось
как сессия, навсегда застрявшая в busy.
Хуки одной сессии выполняются параллельно, поэтому весь цикл «прочитать — решить —
записать» идёт под flock на <session_id>.json.lock, а событие старше
сохранённого отвергается. Нужно и то, и другое: одна лишь проверка меток времени
всё ещё позволяет хуку, прочитавшему старое состояние до Stop, записать своё
устаревшее решение после него.
Сабагенты не показываются. Батч из восьми задач — это одна строка «работает 40 мин», и это правильная строка: пятый воркер из восьми ни о чём не просит.
zellij
Если сессии живут в табах zellij, меню показывает имя таба, а клик
переключает на него. Поиск идёт через zellij action dump-layout — рабочий
каталог сессии сопоставляется с каталогами пейнов, потому что в дампе раскладки
нет id пейнов и ZELLIJ_PANE_ID для этого не годится. Отсюда следствия: две
сессии в одном табе неразличимы, а сессия, сменившая cwd после открытия пейна,
не найдётся. Строки, которые не разрешились, остаются некликабельными — вместо
того чтобы делать вид, будто клик что-то делает.
Если zellij не используется, выключите в настройках: он стоит одного процесса раз в пару минут.
Известные шероховатости
- Протухшие сессии. Убитый терминал не присылает
SessionEnd. Живость перепроверяется каждые 20 с по/proc/<pid>, файл удаляется — убитая сессия исчезает в пределах этого окна, а не висит вечно. Если хук вообще не смог опознать процесс claude, он записывает pid 0 — «неизвестно», что никогда не путается с «мёртв», — и такие записи истекают по возрасту, через 36 часов. Stopне отличает «закончил» от «сдался». Оба читаются как «ждёт ввода», и решение для вас в обоих случаях одно и то же.- Фокус ведёт к табу zellij, а не к окну. gnome-terminal держит все окна под одним общим серверным процессом, так что окно нельзя сопоставить по pid. Окно поднимается, только если в его заголовке есть имя zellij-сессии; когда совпадения нет, таб всё равно переключается, а фокус остаётся на месте — поднять произвольный терминал хуже, чем не поднимать никакой.
Тесты
gjs -m tests/test-sessions.js # чтение состояния, порядок, живость, слежение
tests/test-hook.sh # события -> состояния, блокировка, снос файлов
lib/sessions.js намеренно ничего не импортирует из resource:///org/gnome/shell
— именно это позволяет гонять его в обычном gjs, вне композитора.
Чтобы посмотреть сырые события хуков, создайте файл-маркер, и каждое событие будет дописываться в него:
touch ~/.local/state/claude-code-status/debug