# Navi Code CLI

Терминальный клиент для общения с Navi через WebSocket.

## Установка

CLI устанавливается вместе с пакетом `navi`:

```bash
pip install -e .
```

После установки доступна команда `navi-code`. Внутри репозитория можно запускать без установки:

```bash
python -m clients.terminal
```

## Использование

### Интерактивный режим

```bash
navi-code
```

Клиент подключается к `http://localhost:8000`, создаёт или восстанавливает сессию и запускает чат.

### One-shot режим

```bash
navi-code "объясни, что делает этот файл"
navi-code --new-session "напиши pytest-тест для функции foo"
```

### Параметры

| Флаг | Описание |
|---|---|
| `--base-url URL` | Базовый URL сервера Navi. |
| `--ws-url URL` | URL WebSocket (по умолчанию производный от `--base-url`). |
| `--profile-id ID` | Профиль для новой сессии. |
| `--new-session` | Создать новую сессию, даже если сохранена старая. |
| `--resume <session_id>` | Возобновить конкретную сессию по id (взаимоисключающе с `--new-session`). |
| `--show-thinking` | Показывать блоки рассуждений модели. |
| `--no-events` | Скрывать события `tool_started` / `tool_call`. |
| `--mouse` / `--no-mouse` | Включить/выключить мышь в TUI на один запуск (перекрывает `tui.json`). |
| `--theme NAME` | Запустить TUI с указанной темой. |
| `--raw` | Запустить plain CLI вместо TUI (используется по умолчанию, когда передан позиционный запрос). |
| `--version` | Версия клиента. |
| `-h`, `--help` | Показать справку по использованию. |

Любой позиционный аргумент воспринимается как запрос и запускает plain CLI в one-shot режиме (например `navi-code "объясни этот файл"`). Неизвестные флаги передаются как часть запроса.

При закрытии TUI печатается подсказка вида `Resume with: navi-code --resume <session_id>` — чтобы продолжить ту же сессию в следующий раз.

## Команды в интерактивном режиме

| Команда | Алиасы | Описание |
|---|---|---|
| `/help` | | Список команд. |
| `/new` | `clear` | Создать новую сессию (в TUI `clear` — алиас `new`, а не очистка локального state). |
| `/sessions` | `resume`, `continue` | Открыть picker сессий, отфильтрованный по профилю `navi_code`, с кнопкой «✚ New session» и поиском по типу. |
| `/switch [id]` | | Гибридный режим: без аргумента / неоднозначный префикс → picker, префильтрованный по вводу; точный id или уникальный префикс `navi_code` → мгновенное переключение. |
| `/profile` | | Показать текущий профиль и id сессии. |
| `/export [path]` | `save` | Экспортировать текущую сессию в Markdown; без пути — во временный файл и `$EDITOR`. |
| `/themes` | | Открыть выбор темы с live-preview. |
| `/mouse on\|off` | | Включить/выключить поддержку мыши (требует перезапуска). |
| `/thinking` | | Переключить отображение thinking-блоков в текущей сессии. |
| `/compact` | | Вручную запустить сжатие контекста текущей сессии (шлёт `{"type":"compact"}`). |
| `/quit` | `exit`, `q` | Выйти. |

Подсказки к slash-командам появляются inline над полем ввода при вводе `/`: `Up`/`Down` подсвечивают вариант, `Enter` выполняет подсвеченный, `Tab` дополняет имя.

## Интерфейс TUI

В интерактивном режиме (`navi-code`) экран состоит из пяти регионов:

- **`ChatPanel`** (левая колонка) — история сообщений и карточки событий.
- **Правая колонка** `Vertical(StatusPanel, TodoPanel)`:
  - **`StatusPanel`** — профиль, сессия, текущая модель, статус соединения, backend, тема, hotkey-подсказки.
  - **`TodoPanel`** — живой список todo текущей сессии: `in_progress` наверху, затем `pending`, потом `failed`/`skipped`, `done` внизу. Todo субагентов сюда не попадают (они изолированы). Обновляется по событию `todo_updated`, при подключении подтягивается через REST.
- **`InputBox`** (под чатом) — многострочное поле ввода.
- **`StatusBar`** (нижняя строка, заменяет стандартный Textual Footer) — activity-индикатор (анимированный braille-спиннер + фаза), live-таймер длительности запроса, gauge заполнения контекстного окна (`used/max · pct%`, цвет по уровню), hotkey-комбинации.

Sessions-панели в правой колонке больше нет — переключение сессий через модальный **picker** (`/sessions`, `/switch`).

### Поле ввода

Поле ввода многострочное — перенос текста по ширине автоматический, высота поля растёт вместе с содержимым. `Enter` отправляет сообщение целиком. Префикс `!` выполняет локальную shell-команду (passthrough, не уходит на сервер). `@путь` раскрывает ссылку на файл и прикрепляет его к сообщению.

### Рендеринг чата

- **Per-message виджеты.** Каждое сообщение — отдельный виджет; при стриминге перерисовывается только активный пузырь, а не весь список.
- **Markdown.** Ответы Navi рендерятся как themed markdown с подсветкой кода, прямо во время стриминга.
- **Ограничение стоимости рендера.** Видимых элементов ограничено (`max_visible_items`); более старые сворачиваются в подсказку «… N earlier messages not shown». Рендер кэшируется по сигнатуре.
- **История при resume.** При возобновлении/переключении сессии история сообщений подгружается в чат через REST.
- **Auto-reconnect.** При обрыве WebSocket переподключается с экспоненциальным backoff (1с→30с); отправленные во время обрыва сообщения переотправляются, в `StatusPanel` показывается «reconnecting…».

### Карточки в чате

- **Планирование.** Пока работает планировщик — rolling-индикатор `⚙ Planning · <фаза>` (обновляется на месте). Когда план готов — карточка `Plan`.
- **Субагент (`spawn_agent`).** Карточка `↘ subagent · <профиль>` при запуске и `↙ subagent · <профиль> ✓/✗` с результатом; задача и брифинг — в теле.
- **Вложенные события субагента.** Рассуждения, планирование, карточки инструментов и todo субагента — с отступом и приглушённым стилем (плоская модель, вложенность через отступ).
- **Compression summary.** При сжатии контекста (авто или `/compact`) — карточка `Context compressed: N → M messages` с dim markdown-текстом саммари внутри.
- **Метаданные хода.** На `stream_end` — строка `⏱ …` с длительностью запроса и gauge заполнения контекста.

### Диалоги и палитра

- **Command palette** (`Ctrl+P`) — поиск команд по имени.
- **Permission dialog** — запрос подтверждения на запуск инструмента: allow once/always, deny once/always.
- **Sessions picker** — модальный выбор сессии.
- **Theme picker** — выбор темы с live-preview.

### Hotkeys

| Комбинация | Действие |
|---|---|
| `Ctrl+P` | Command palette |
| `Ctrl+X C` | `/compact` (сжатие контекста) |
| `Ctrl+X T` | `/thinking` (toggle thinking-блоков) |
| `Ctrl+X Q` | `/quit` |
| `Esc` | Остановить стрим |

## Состояние

Клиент сохраняет `session_id` в `~/.navi_code/state.json`, чтобы восстановить диалог при следующем запуске. Удалите файл или используйте `--new-session`, чтобы начать с чистого листа. TUI-настройки (тема, мышь, `max_visible_items`) хранятся в `~/.navi_code/tui.json`.

## Рендеринг событий

События WebSocket рендерятся по-разному в TUI и в plain CLI (`--raw`):

- `stream_start` — запускает activity-индикатор и live-таймер.
- `stream_delta` — печатается inline, как в обычном чате.
- `stream_end` — метаданные хода (`⏱` длительность + gauge контекста).
- `stream_stopped` — отметка об остановке пользователем.
- `tool_started` / `tool_call` — карточки инструментов: имя, аргументы, краткий результат (если не задан `--no-events`). В `metadata.step_text` для todo-update передаётся текст изменённого шага.
- `thinking_delta` / `thinking_end` / `turn_thinking` — блоки рассуждений (только с `--show-thinking`); `turn_thinking` — полный блок субагента целиком.
- `planning_status` — rolling-строка фазы (в plain CLI — `[planning] <фаза>`).
- `plan_ready` — карточка `Plan` (в plain CLI — `[plan]…[/plan]`).
- `model_info` — обновляет модель в `StatusPanel`.
- `todo_updated` — обновляет `TodoPanel`.
- `compression_started` — activity-фаза «compression».
- `context_compressed` — карточка `Context compressed: N → M messages` с саммари.
- `error` — красным цветом.
- `heartbeat` / `session_sync` — служебные, в чат не выводятся.

## Пример сессии

```bash
$ navi-code
Created session a1b2c3d4 (profile navi_code)
Navi Code interactive mode. Type /quit to exit, /help for commands.
You: напиши скрипт, который считает строки в текущей директории
[tool: terminal]
{'cmd': 'find . -type f | wc -l'}
[tool result: terminal success=True]
42

Я насчитал 42 файла в текущей директории и её поддиректориях.
You: /quit
Resume with: navi-code --resume a1b2c3d4
```

## Разработка

Код CLI находится в `clients/terminal/`:

- `cli.py` — точка входа, разбор аргументов, raw-режим, resume-hint.
- `ws_client.py` — низкоуровневый WebSocket-клиент.
- `api.py` — REST-запросы к серверу.
- `render.py` — рендеринг событий в терминал (plain CLI).
- `state.py` — сохранение `session_id` в `~/.navi_code/state.json`.
- `config.py` — настройки из переменных окружения `NAVI_CODE_*`.
- `tui/tui_app.py` — Textual-приложение, bindings, диспетчер событий.
- `tui/ws_bridge.py` — владеет жизненным циклом WebSocket в TUI (включая auto-reconnect).
- `tui/chat_model.py` — модель чат-айтемов и преобразование WS-событий.
- `tui/widgets/` — `ChatPanel`, `InputBox`, `CommandHints`, `StatusBar`, `StatusPanel`, `TodoList` (+ `ActivityIndicator`, `ElapsedTime`).
- `tui/renderers/` — рендереры карточек по типам событий (сообщения, markdown, рассуждения, инструменты, планирование, субагент, todo, summary, turn_meta, diff, artifact, error); диспетчеризация через `RendererRegistry`.
- `tui/commands/` — slash-команды (`builtin.py`: `/help`, `/new`, `/sessions`, `/switch`, `/profile`, `/export`, `/themes`, `/mouse`, `/thinking`, `/compact`, `/quit`).
- `tui/screens/` — модальные экраны: `command_palette`, `sessions_picker`, `theme_picker`, `permission_dialog`.
- `tui/permissions.py`, `tui/shell_runner.py`, `tui/file_refs.py`, `tui/duration.py`, `tui/context.py`, `tui/events.py` — диалоги разрешений, `!`shell, `@`file-refs, таймеры, контекст, вспомогательные события.
- `tui/settings.py` — persisted TUI config (`~/.navi_code/tui.json`).
- `tui/themes.py` — палитры тем.

Тесты: `tests/clients/test_terminal_client.py`, `tests/clients/test_terminal_ws.py`, `tests/clients/test_tui_*.py`.
