Files
claude-code-gnome-extension/README.md
T
av 58409106c3 Перевести README на русский
Единственный читатель этого расширения пишет и думает по-русски.
Строки интерфейса, комментарии в коде и сообщения коммитов остаются
английскими: их адресат — GNOME Shell и потенциальный сторонний
читатель кода, а не владелец репозитория.
2026-08-09 18:26:53 +03:00

136 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Claude Code Status
Индикатор для GNOME Shell, отвечающий на один вопрос: **какая сессия Claude
Code ждёт меня прямо сейчас?**
Когда открыто несколько сессий в разных проектах, дорого не «не знать, чем
каждая занята», а не заметить, что одна из них встала час назад. Панель
называет сессию, а не только состояние.
## Состояния
| В панели | Состояние | Что значит |
|---|---|---|
| диск в кольце | `blocked` | упёрлась в запрос разрешения — без вас не сдвинется |
| закрашенный диск | `waiting` | ход закончен, ждёт вашего ввода |
| кольцо | `busy` | работает |
| тусклое пунктирное кольцо | `idle` | запущена, но ничего не просили, либо ничего не запущено |
`blocked` и `waiting` разведены намеренно. Слитые в одно «требует внимания»,
законченная задача выглядит так же срочно, как заблокированная, — а именно это
различие и решает, переключаться сейчас или после текущей мысли.
Панель показывает **самую приоритетную** сессию и счётчик `+N`, если в том же
состоянии есть другие. Внутри состояния выигрывает **самая давняя**: забывается
та, что ждёт дольше всех, а не последняя.
**Уведомлений на рабочий стол нет** — сознательно. `notify-send` из хука было бы
куда дешевле сделать, но форма неверная: сессия, ждущая двадцать минут, должна
оставаться на виду, а уведомление исчезает в момент, когда его смахнули. Панель
здесь — единственный канал.
## Установка
```sh
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-сессии; когда
совпадения нет, таб всё равно переключается, а фокус остаётся на месте —
поднять произвольный терминал хуже, чем не поднимать никакой.
## Тесты
```sh
gjs -m tests/test-sessions.js # чтение состояния, порядок, живость, слежение
tests/test-hook.sh # события -> состояния, блокировка, снос файлов
```
`lib/sessions.js` намеренно ничего не импортирует из `resource:///org/gnome/shell`
— именно это позволяет гонять его в обычном `gjs`, вне композитора.
Чтобы посмотреть сырые события хуков, создайте файл-маркер, и каждое событие
будет дописываться в него:
```sh
touch ~/.local/state/claude-code-status/debug
```