Andrey Epifancev c810f5a880 fix: pass shell PATH to panel service so tmux panes find lf (#4, H-1)
The launchd service runs with PATH=/usr/bin:/bin:/usr/sbin:/sbin; tmux
split-window gives the new pane the caller's PATH, so 'exec lf' failed
and the pane closed at once. install.sh now records its own PATH in the
plist EnvironmentVariables.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 22:44:58 +04:00

72 lines
7.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.

# cc-agents-iterm-plugin
## Что это
Панель «Claude» — страница на `http://127.0.0.1:8766/`. Сверху — дерево активных сессий Claude Code
и их субагентов (название, модель, размер контекста, worktree, статус), снизу — лимиты подписки
(5 ч, неделя, разбивка по продуктам) и баланс extra usage. Её показывает iTerm2 Toolbelt и
браузер — в том числе Safari на iPad через SSH-туннель Termius. Для одного пользователя на его
Mac: видеть, что делают все запущенные `claude` (в tmux), сколько осталось лимитов, и в один клик
переходить в нужную панель tmux.
## Стек и ограничения
- Python 3.8+, только стандартная библиотека; модуль `iterm2` нужен только регистратору Toolbelt
(его даёт iTerm2). Без зависимостей из pip — пользователь ставит панель `./install.sh`.
- Хук — POSIX `sh` + `jq`. Хук никогда не мешает Claude Code: ничего не выводит, всегда `exit 0`.
- macOS, launchd. Токен OAuth берётся из Keychain (`Claude Code-credentials`) или
`~/.claude/.credentials.json`, поэтому служба работает в GUI-сессии пользователя.
- Сервер слушает только `127.0.0.1` — в панели видны тексты сообщений сессий.
- Установщик не трогает чужие хуки и настройки: правит `~/.claude/settings.json` только через
`settings_hooks.py` (маркер `agents_panel_hook`), перед заменой кладёт копии в
`~/.claude-panel-backup/<дата>/`. Повторный запуск `install.sh` идемпотентен. Запуск по SSH
`install.sh`/`uninstall.sh` отклоняют.
## Архитектура
- `files/agents_panel_hook.sh` — хук Claude Code на SessionStart/End, UserPromptSubmit, Stop,
Notification, SubagentStart/Stop, PostModelSwitch, PreToolUse(Agent|Task). Дописывает событие
в `~/.cache/claude-agents/<session_id>.jsonl` вместе с PID процесса `claude`,
`ITERM_SESSION_ID` или сокетом/панелью tmux.
- `files/claude_agents.py` — один файл, два режима (`run`):
- `--headless` (`serve`) — служба launchd `com.andrey.claude-agents`, единственный владелец порта:
- `refresher` раз в `poll_sec` перечитывает jsonl (`load_sessions` → `replay`), сверяет с
процессами (`ps`) и панелями tmux, дочитывает из транскриптов модель и токены, из git —
worktree; собирает `state["tree"]` (`build_tree`, без данных iTerm2).
- `usage_poller` / `balance_poller` ходят в `api.anthropic.com/api/oauth/usage` и
`.../prepaid/credits` (запасной путь — claude.ai с `sessionKey` из Keychain).
- HTTP-сервер (`handle`): `/`, `/fragment` (дерево), `/limits`, `/message` (последние
сообщения), `/activate` и `/open-wt` (через tmux), `/raw` (отладка).
- без флага (`register_toolbelt`) — скрипт AutoLaunch iTerm2: регистрирует web view «Claude» на
адрес службы; если порт молчит — `launchctl kickstart`. Свой сервер не поднимает.
- `files/launchagent.py` — пишет plist службы (`write <plist> <python> <script> <log> <PATH>`; PATH берётся из оболочки `install.sh`, его наследуют панели tmux, которые открывает служба).
- `files/settings_hooks.py` — добавляет/убирает хуки панели в `settings.json` (`add` / `remove`).
- `files/config.example.json` — все ключи настроек; рабочий файл `~/.config/claude-agents/config.json`
перечитывается на лету, кроме `port`.
- `install.sh` / `uninstall.sh [--purge]` — раскладывают файлы, ставят/убирают службу (таблица в README.md).
## Команды
- `./install.sh` — установить или обновить у себя (за самим Mac); затем Toolbelt → Claude и перезапуск сессий `claude`.
- `./uninstall.sh` — удалить (`--purge` — вместе с настройками и логами).
- `python3 -m unittest discover -s tests -v` — весь набор тестов (stdlib, без pytest; `node` нужен
для теста клиентского JS). Зелёным считается только весь набор.
- `python3 -m py_compile files/*.py && sh -n files/agents_panel_hook.sh && bash -n install.sh && bash -n uninstall.sh` —
синтаксическая проверка; сборки нет.
- `curl -s http://127.0.0.1:8766/raw` — что видит запущенная панель: логи хука, причины скрытия
сессий, ошибки лимитов и баланса. Лог службы — `~/Library/Logs/claude-agents/panel.log`,
состояние — `launchctl print gui/$(id -u)/com.andrey.claude-agents`.
## Тесты
- `tests/` — `unittest`. `claude_agents.py` импортируется без iTerm2 (`iterm2 = None`). Сеть,
Keychain, launchd, `ps` и tmux в тестах подменяются; реальные `~/.claude` и `~/Library` не трогаются.
- `install.sh` и `uninstall.sh` в тестах запускаются только с `SSH_CONNECTION`/`SSH_TTY` (проверка
отказа): без охраны они остановили бы настоящую панель на порту 8766.
## Где лежит код
- Код — в `files/`; хук require-plan защищает его через `.claude/code-paths`. `install.sh` и
`uninstall.sh` в корне хуком не покрыты (он умеет только каталоги).
## OpenSpec и superpowers
- Вход в любую задачу — superpowers:brainstorming. Skills openspec-propose и openspec-explore сами по себе не запускать.
- Когда brainstorming классифицировал задачу как architectural, структуру change создать через `openspec new change <id>` (или `/opsx:propose <id>`), а содержимое proposal.md, specs/ и design.md написать по итогам brainstorming. tasks.md из OpenSpec не заполнять — задачи живут в плане superpowers.
- `/opsx:apply` не использовать: исполнение идёт через writing-plans и SDD/executing-plans.
- `openspec archive <id>` — после merge PR, как часть чеклиста закрытия.
- Хук require-plan защищает любой каталог `src/` на любой глубине. Если код лежит иначе — перечислить каталоги (префиксы от корня, по одному в строке) в `.claude/code-paths`.