beads-hud: задачи и документы проекта там, куда я и так смотрю

ИнструментыИИCLI

Я работаю с ИИ-агентами в терминале, а задачи держу в beads — трекере, который живёт локальной базой прямо в репозитории. Связка хорошая, но у неё есть слабое место, и оно не в трекере.

Терминал плохо отвечает на два вопроса: что написано в планах этого проекта и как задачи связаны между собой. bd list печатает плоский список, и связь задачи с эпиком теряется в нём первой. А между сессиями агента я теряю нить: какой эпик в работе, что кого держит, где лежит спека, к которой это всё относится.

Читать вывод команды каждый раз, когда возник вопрос, — это трение. Настолько маленькое, что его не замечаешь, и настолько частое, что за день оно съедает больше, чем кажется.

Поэтому я перенёс ответ туда, куда и так смотрю двести раз за сессию, — в строку статуса.

⏽ beads-hud http://127.0.0.1:7777 │ bd 20 готово · 2 в работе · 5 ждут │ ctx 43% │ 5ч 38% 1ч19м │ нед 8% │ $34.34 │ Opus 5·1M xhigh

Первый сегмент — живая ссылка на доску, поднятую для этой папки. Второй — состояние трекера. Дальше контекст, лимиты, деньги и модель: их Claude Code сам подаёт скрипту на вход, ничего не опрашивается и не угадывается.

По ссылке открывается доска: задачи в четырёх колонках по статусу и все .md файлы проекта, читаемые и правимые на месте. Строка и доска — не два инструмента, а один: строка знает, что доска поднята, а хук поднимает её при старте сессии.

Почему строка не ждёт трекер

Первая версия просто звала bd stats при каждой отрисовке. Замер: 0.4 секунды. Строка статуса перерисовывается на каждый ход — это неприемлемо, тормозить будет всё.

Очевидный ответ — демон, который держит числа свежими. Я его не стал делать. Демон надо запускать, следить, что он жив, убивать при обновлении, чинить, когда он подвиснет на заблокированной базе. Это отдельный кусок работы и отдельный источник проблем — ради трёх чисел.

Вместо этого строка печатает кэш и, если кэшу больше тридцати секунд, отцепляет обновление себе за спину. Обновление живёт ровно столько, сколько занимает одна команда. Ничего не надо обслуживать: пропал трекер — числа перестали меняться, вернулся — поехали дальше. Пока кэша нет, сегмента нет вовсе — вместо нуля, который выглядит как «всё сделано».

Заодно решился вопрос, на каком языке это писать. Пакет ставится через npm, Node уже есть, соблазн был переписать всё на нём и убрать зависимость от jq. Замерил: пустой запуск Node — 36 мс, а вся отрисовка на sh вместе с чтением кэша и разбором JSON — 20 мс. То есть Node проиграл, ещё не начав работать. Остался shell.

Почему доска не ждёт трекер

Та же болезнь была на доске, только заметнее. Переключаешь папку — и секунд десять смотришь на предыдущий проект, потому что ответ собирался целиком: обход всех .md плюс три вызова трекера.

Половины стоят разного. Замер на большом рабочем проекте — 384 задачи, 174 файла:

Что Сколько
Обход и разбор всех .md 0.6 с
Опрос трекера 14 с

Я разделил ответ надвое. Документы приходят почти сразу, задачи — когда придут, и доска в это время честно говорит, что считает их. Четырнадцать секунд никуда не делись — но теперь они проходят с открытыми документами, а не перед застывшим экраном.

Это тот случай, когда «сделать быстрее» невозможно, а «перестать ждать» — вполне.

Правка документа без «сохранить файл»

Документы не только читаются, но и правятся. Клик по абзацу превращает его в исходный markdown, Ctrl+Enter сохраняет.

Простой путь — держать в редакторе весь файл и писать его целиком. Я так не сделал: сервер режет документ на блоки и запоминает точные смещения каждого. Правка вклеивается по смещениям, поэтому всё, что вне абзаца, остаётся в файле байт в байт — ни переносов строк, ни пробелов в конце, ни нормализации.

Разница видна на первом же файле с блоком кода: наивная склейка любит съесть пустую строку внутри тройных кавычек. У сплиттера есть отдельная проверка, которую можно запустить одной командой, — ровно потому, что ошибка тут молча съедает соседний абзац.

Честный долг

У правки нет отката. Я узнал это неприятным способом: агент, гонявший тесты, промахнулся папкой и переписал три абзаца в README проекта. Файл был вне git — восстанавливал по логам и скриншотам. Сам инструмент отработал ровно как задумано, вклеил ровно то, что просили. Просто «отработал как задумано» и «то, чего я хотел» — разные вещи, а слоя, который позволяет передумать, не было. Он нужен: либо git в папке, либо копия предыдущей версии рядом.

Связи видны только у открытых задач. Трекер отдаёт граф зависимостей без закрытых, поэтому у закрытой задачи не видно, что её держало, а эпик с полностью закрытыми детьми выглядит бездетным.

Строка не заработает в нативной Windows. Это shell-скрипт. WSL, Linux, macOS — да; голая Windows — нет, и обходить это я не планирую.

Ставится так:

npm i -g @roflochinsky/beads-hud

Дальше две настройки в ~/.claude/settings.json — строка статуса и хук автозапуска. Исходники и инструкция: github.com/Roflochinsky/beads-hud.

← Все посты