diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index d35acb8..086af0d 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,15 +1,17 @@ # Архитектура -Два процесса, связанные только файлами в `~/.cache/claude-agents/`: хук внутри каждой сессии -Claude Code пишет события, скрипт iTerm2 их читает и рисует панель. +Три части, связанные файлами в `~/.cache/claude-agents/` и адресом `127.0.0.1:8766`: хук внутри +каждой сессии Claude Code пишет события, служба launchd их читает и отдаёт страницу, скрипт iTerm2 +показывает эту страницу в Toolbelt. iTerm2 для работы панели не нужен. ``` claude (каждая сессия) ──hook──▶ agents_panel_hook.sh ──append──▶ ~/.cache/claude-agents/.jsonl │ -iTerm2 AutoLaunch: claude_agents.py ◀───── read (poll 1.5 s) ────────────┘ - ├─ + iTerm2 API (вкладки, профиль), ps, tmux, транскрипты ~/.claude/projects, git +launchd: claude_agents.py --headless ◀──── read (poll 1.5 s) ────────────┘ + ├─ + ps, tmux, транскрипты ~/.claude/projects, git ├─ + api.anthropic.com oauth/usage, prepaid/credits (OAuth из Keychain) - └─ HTTP 127.0.0.1:8766 ──▶ web view tool «Claude» в Toolbelt + └─ HTTP 127.0.0.1:8766 ──▶ web view «Claude» в Toolbelt (регистрирует claude_agents.py в iTerm2) + └─▶ браузер; с iPad — SSH local forward в Termius ``` ## Модули @@ -17,19 +19,23 @@ iTerm2 AutoLaunch: claude_agents.py ◀───── read (poll 1.5 s) ── | Модуль | Ответственность | Зависит от | |---|---|---| | `files/agents_panel_hook.sh` | Событие хука → строка jsonl с PID, вкладкой iTerm2 или панелью tmux | `jq`, `ps` | -| `files/claude_agents.py` — сессии | `load_sessions`/`replay`: jsonl → состояние сессий и субагентов; `build_tree` сводит с живыми вкладками, процессами, tmux | файлы хука, iTerm2 API, `ps`, `tmux`, транскрипты, `git` | +| `files/claude_agents.py` — сессии | `load_sessions`/`replay`: jsonl → состояние сессий и субагентов; `build_tree` сводит с процессами и tmux | файлы хука, `ps`, `tmux`, транскрипты, `git` | | `files/claude_agents.py` — лимиты | `fetch_usage`/`fetch_balance` + `parse_limits`/`parse_balance` | Keychain/`~/.claude/.credentials.json`, `~/.claude.json`, api.anthropic.com, claude.ai | -| `files/claude_agents.py` — UI | `render_*`, `handle` (HTTP), `activate`/`open_worktree`, `theme_poller` | `state`, iTerm2 API | +| `files/claude_agents.py` — UI | `render_*`, `handle` (HTTP), `activate`/`open_worktree` (tmux) | `state` | +| `files/claude_agents.py` — запуск | `run`: `serve` (служба, `--headless`) или `register_toolbelt` (iTerm2) | launchd, `iterm2` | +| `files/launchagent.py` | plist службы `com.andrey.claude-agents` | — | | `files/settings_hooks.py` | Идемпотентно добавить/убрать хуки в `~/.claude/settings.json` | — | -| `install.sh` / `uninstall.sh` | Разложить/убрать файлы, включить Python API iTerm2, перезапустить панель | `settings_hooks.py` | +| `install.sh` / `uninstall.sh` | Разложить/убрать файлы, поставить/снять службу, включить Python API iTerm2 | `settings_hooks.py`, `launchagent.py`, `launchctl` | ## Границы - Хук не зависит от панели и не должен влиять на Claude Code: без вывода, всегда `exit 0`. - Панель только читает данные Claude Code (jsonl, транскрипты, `~/.claude.json`) и ничего туда не пишет; её единственная запись — удаление собственных логов старше `keep_logs_days`. -- `claude_agents.py` — только стандартная библиотека + `iterm2`; импорт без iTerm2 должен работать. +- Порт занимает только служба; скрипт iTerm2 сервер не поднимает и данные iTerm2 не передаёт. +- `claude_agents.py` — только стандартная библиотека + опционально `iterm2`; импорт без iTerm2 должен работать. - HTTP слушает только `127.0.0.1`. +- Фон панели выбирается на клиенте и хранится в браузере/web view; сервер о нём не знает. ## Открытые вопросы -- Docstring `claude_agents.py` ссылается на несуществующий `settings-hooks.json` (теперь это `settings_hooks.py` + `install.sh`). -- Автотестов нет; `claude_agents.py` — один файл на 1500 строк. +- После перезагрузки Mac без входа на нём (только SSH) служба не стартует, Keychain заблокирован. +- `claude_agents.py` — один файл на ~1400 строк. diff --git a/CLAUDE.md b/CLAUDE.md index e932e10..591f276 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,55 +1,63 @@ # cc-agents-iterm-plugin ## Что это -Панель «Claude» для iTerm2 Toolbelt. Сверху — дерево активных сессий Claude Code и их субагентов -(название, модель, размер контекста, worktree, статус), снизу — лимиты подписки (5 ч, неделя, -разбивка по продуктам) и баланс extra usage. Для одного пользователя на его Mac: видеть, что -делают все запущенные `claude` и сколько осталось лимитов, и в один клик переходить в нужную -вкладку или панель tmux. +Панель «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` предоставляет сам iTerm2 - (скрипт AutoLaunch). Без зависимостей из pip — пользователь ставит панель `./install.sh`. +- Python 3.8+, только стандартная библиотека; модуль `iterm2` нужен только регистратору Toolbelt + (его даёт iTerm2). Без зависимостей из pip — пользователь ставит панель `./install.sh`. - Хук — POSIX `sh` + `jq`. Хук никогда не мешает Claude Code: ничего не выводит, всегда `exit 0`. -- Только macOS + iTerm2 с включённым Python API. Токен OAuth берётся из Keychain - (`Claude Code-credentials`) или `~/.claude/.credentials.json`. +- 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` идемпотентен. + `~/.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/.jsonl` вместе с PID процесса `claude`, `ITERM_SESSION_ID` или сокетом/панелью tmux. -- `files/claude_agents.py` — скрипт iTerm2 AutoLaunch, один файл: - - `refresher` раз в `poll_sec` перечитывает jsonl (`load_sessions` → `replay`), сверяет с живыми - сессиями iTerm2, процессами (`ps`) и панелями tmux, дочитывает из транскриптов модель и токены, - из git — worktree; собирает `state["tree"]` (`build_tree`). - - `usage_poller` / `balance_poller` ходят в `api.anthropic.com/api/oauth/usage` и - `.../prepaid/credits` (запасной путь — claude.ai с `sessionKey` из Keychain). - - HTTP-сервер на `127.0.0.1:8766` (`handle`) отдаёт страницу, которую iTerm2 показывает как web view - tool: `/`, `/fragment` (дерево), `/limits`, `/message` (последние сообщения), `/activate`, - `/open-wt`, `/raw` (отладка). - - `theme_poller` подстраивает цвета под профиль iTerm2. +- `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