# Индикатор статуса Claude Code в панели GNOME Заметка для быстрого старта. Написана до кода — здесь замысел и проверенные факты об окружении, а не описание существующего. ## Задача Знать, **когда переключиться** на терминал с Claude Code и **на какой именно**, не глядя в сами терминалы. Состояний три: работает / ждёт меня / тихо. Сессий одновременно несколько (разные проекты, разные терминалы) — это основной режим, а не краевой случай. ## Решения, принятые заранее - **Уведомлений не будет.** `notify-send` отвергнут сознательно, хотя он и был бы дешевле. Следствие: панель — единственный канал, значит расширение делается сразу, «сначала уведомления, потом может быть индикатор» отпадает. - **Панель называет проект, а не только состояние.** «Кто-то ждёт» оставляет гадать, в каком из четырёх терминалов; знать «когда» без «куда» бесполезно. - **Сабагенты внутри сессии не показываются.** Батч из восьми задач — это одна строка «работает 40 мин», и это правильная строка: пятый воркер из восьми ни о чём не просит. Хук `SubagentStop` для этого существует, но вешать на него статус — шум. Если понадобится прогресс внутри батча (сделано N из 8), брать его надо из учёта задач, а не из хуков. ## Окружение (проверено 2026-08-07) - GNOME Shell **46.0**, сессия **Wayland**, `XDG_CURRENT_DESKTOP=ubuntu:GNOME`. - `~/.claude/settings.json`: ключа `hooks` нет вовсе (`{}`) — место чистое, ничего не сломаем. `statusLine` занят: `bash ~/.claude/statusline-command.sh`. ## Шаблон — своё же расширение `~/projects/private/sing-box-gnome-extension`, оно же `sing-box-status@git.vakhrushev.me`. Установлено **симлинком** из репозитория в `~/.local/share/gnome-shell/extensions/` — так же ставим и это. Скелет копируется целиком: ``` extension.js 16 строк: enable() → Main.panel.addToStatusArea(uuid, indicator) lib/indicator.js панель и меню lib/format.js форматирование prefs.js настройки schemas/ gschema.xml + gschemas.compiled metadata.json "shell-version": ["45","46","47","48"] stylesheet.css ``` Меняется только источник данных: вместо опроса Clash API по HTTP — **`Gio.FileMonitor`** на каталоге состояния. Это push, поллинг не нужен, выходит проще оригинала. Предполагаемый uuid: `claude-code-status@git.vakhrushev.me`. ## Источник данных — хуки Claude Code Пишутся в глобальный `~/.claude/settings.json`, чтобы работало во всех проектах. | Хук | Состояние | |---|---| | `SessionStart` | сессия появилась | | `UserPromptSubmit` | работает | | `Notification` | ждёт меня — разрешение или простой на вводе | | `Stop` | ход закончен, ждёт ввода | | `SessionEnd` | сессия исчезла | `Notification` и `Stop` **разделять в панели**: первое горит (заблокирована на разрешении), второе просто ждёт (задача сделана). Слитые в одно, законченная задача выглядит так же срочно, как заблокированная. ### Файл состояния Один файл на сессию, иначе параллельные сессии затирают друг друга: ``` ~/.local/state/claude-code-status/.json ``` Поля: состояние, `cwd`, метка времени последней смены, `$PPID` для проверки живости. `SessionEnd` файл удаляет. **Проверить на первом же прогоне** (по памяти, не подтверждено): хук получает на stdin JSON с `session_id`, `cwd`, `transcript_path`, `hook_event_name`. Имена полей сверить с реальным вводом, а не доверять этой строке. Названия событий `SubagentStop`, `SessionEnd`, `UserPromptSubmit` подтверждены — встречаются в конфиге плагина wakatime (`~/.claude/plugins/cache/wakatime/`), там же можно подсмотреть рабочий пример hooks.json. ## Правила отображения Приоритет агрегации: **хоть одна ждёт → «ждёт»**, иначе **хоть одна работает → «работает»**, иначе тихо. - Показывается **дольше всех ждущая** сессия, не последняя: последняя и так свежа в голове, забывается именно давняя. - Несколько ждущих — счётчиком: `✋ dev-skills +2`. - Для работающей полезно «работает 6 мин» — по этому решаешь, ждать или уходить. - В меню — список сессий с **полным путём**: два worktree одного репозитория по basename не различаются. ## Известные шероховатости - **Протухание.** Убитый терминал не пришлёт `SessionEnd`, файл останется. Лечится меткой времени плюс проверкой живости `$PPID` — не идеально, но практично; индикатор гасит протухшие. - **Клик в меню не переключит фокус** на нужный терминал: на Wayland расширению это просто так не даётся. Меню информационное. - **`Stop` не отличает «закончил» от «упал»** — оба выглядят как «ждёт ввода». Для задачи «пора переключиться» разницы нет, но знать стоит. ## Порядок работ 1. Хуки и формат файла состояния — в `~/.claude/settings.json`. 2. **Проверить на живых сессиях** через `watch cat`, что состояния переключаются правильно и поля stdin те, что ожидались. Здесь же выяснится, когда реально срабатывает `Notification`. 3. Индикатор поверх заведомо верных данных. Порядок именно такой: отлаживать GJS и раскладку состояний одновременно — это две неизвестные в одном уравнении.