Claude Code пишет каждую сессию в ~/.claude/projects/<путь-проекта>/<uuid>.jsonl. Формат машинный: thinking, tool_use и результаты инструментов лежат разными типами блоков, связи между сообщениями идут через parentUuid, а tool_result формально приходит ролью user. Через jq это читается, но недолго.

Claude Code History Viewer (jhlee0409/claude-code-history-viewer, MIT, Rust + Tauri) собирает из этих файлов интерфейс: диалоги с тулколлами, глобальный поиск, статистику по токенам и стоимости. Кроме Claude Code он понимает историю ещё 27 клиентов — Codex CLI, Gemini CLI, Cursor, Cline, Copilot, Goose, Zed и прочих.

Транскрипты обрабатываются локально, телеметрии проект не заявляет (десктопная версия при этом ходит на GitHub за обновлениями). Режима два: обычное приложение (.dmg, .exe, .AppImage, .deb, .rpm, brew install --cask jhlee0409/tap/claude-code-history-viewer) и headless-сервер, который отдаёт тот же интерфейс в браузер. Дальше — про второй: история у меня живёт на Linux-сервере без иксов. Версия на момент проверки — 1.22.0.

Запуск в Docker

В репозитории есть свой docker-compose.yml, но он собирает бинарь из исходников: Rust, pnpm, webkit-заголовки. Возьмём готовый бинарь из релиза — для x86_64, под ARM64 в релизе лежит отдельный архив:

mkdir cchv && cd cchv
curl -fsSLO https://github.com/jhlee0409/claude-code-history-viewer/releases/download/v1.22.0/cchv-server-linux-x64.tar.gz
curl -fsSLO https://github.com/jhlee0409/claude-code-history-viewer/releases/download/v1.22.0/CHECKSUMS.sha256
sha256sum -c --ignore-missing CHECKSUMS.sha256

Рядом создаём файл Dockerfile. Распаковывать архив не нужно, ADD делает это сам:

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
      ca-certificates curl libgtk-3-0 libwebkit2gtk-4.1 libjavascriptcoregtk-4.1-0 \
    && rm -rf /var/lib/apt/lists/*
ADD cchv-server-linux-x64.tar.gz /usr/local/bin/
ENTRYPOINT ["cchv-server", "--serve", "--host", "0.0.0.0", "--port", "3727"]

Gtk и webkit нужны даже серверному бинарю: это compile-time зависимость Tauri, окно в режиме --serve не открывается. Сборка образа у меня заняла 2 минуты 27 секунд, почти всё — apt.

docker build -t cchv-server:1.22.0 .
install -d -m 700 "$HOME/.claude-history-viewer"
TOKEN="$(openssl rand -hex 24)"
echo "http://127.0.0.1:3727/?token=$TOKEN"

docker run -d --name cchv --restart unless-stopped \
  --user "$(id -u):$(id -g)" -e HOME=/home/cchv \
  -v "$HOME/.claude:/home/cchv/.claude:ro" \
  -v "$HOME/.claude-history-viewer:/home/cchv/.claude-history-viewer" \
  -p 127.0.0.1:3727:3727 --memory 1g \
  cchv-server:1.22.0 --token "$TOKEN"

Второй том — не украшение: туда сервер кладёт токен и туда же складывает архивы (о них ниже). Читать историю остальных клиентов — монтируйте их каталоги отдельно, тоже read-only.

Про токен стоит сказать подробнее, потому что README тут отстал от кода. Он обещает, что при старте печатается полный токен и готовый URL с ?token=.... В 1.22.0 в лог уходят первые восемь символов, а сам токен сохраняется файлом:

🔑 Auth token enabled: 9547b40b...
   Generated token saved to: /home/cchv/.claude-history-viewer/webui-token.txt

Если домашний каталог смонтирован read-only, записать файл сервер не сможет и напишет Failed to persist generated token. Re-run with --token <value>. Тогда войти нельзя вообще: в логе только восемь символов. Отсюда и --token в примере выше.

Проверяем сервер и открываем напечатанный URL (снаружи — через ssh -L 3727:127.0.0.1:3727 user@host):

$ curl -s http://127.0.0.1:3727/health
{"status":"ok"}

Что внутри

Слева дерево проектов и сессий со счётчиками, справа выбранный диалог: сообщения, тулколлы, thinking, дифы, вывод команд с ANSI-цветами. Сабагенты идут отдельными ветками, по правому клику копируются id сессии, путь к файлу и готовая команда claude --resume.

Ctrl+K — поиск сразу по всем провайдерам; в результатах видно, какой сессии принадлежит совпадение.

Отдельный пункт над списком проектов — Global Statistics.

22,5 млрд токенов, 414,8 тыс. сообщений, 9862 сессии, 54 дня чистого времени сессий. Оценка стоимости — 12 824 доллара, если бы это шло по прайсу API. Рядом плашки «Estimated» и «Pricing coverage: 35,4%»: посчитаны только те модели, для которых нашлась цена. Разбивка Billing показывает, что на диалог приходится 68,3% токенов, а 31,7% — на инструменты и системный трафик.

Ниже на той же странице — топ инструментов, топ моделей, heatmap активности по дням и разбивка по скилам и сабагентам с именами.

Дёргать по HTTP

Бэкенд не спрятан: те же команды доступны как POST /api/<имя> с Bearer-токеном. Без токена — честный 401.

curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' -d '{}' \
  http://127.0.0.1:3727/api/scan_all_projects |
  jq -r '.[] | [.name, .path, .session_count] | @tsv'

Путь из второй колонки нужен для get_expiring_sessions — это список сессий, которые вот-вот попадут под автоочистку Claude Code (порог по умолчанию — 7 дней, меняется полем thresholdDays):

curl -s -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"projectPath":"'"$HOME"'/.claude/projects/-home-user-myproject"}' \
  http://127.0.0.1:3727/api/get_expiring_sessions |
  jq -r 'length, ([.[] | select(.daysRemaining <= 1)] | length)'

По одному моему проекту в списке 61 сессия, пять из них уходят в ближайшие сутки. Механика простая: cleanupPeriodDays по умолчанию 30, чистка идёт при старте клиента, и вьюер покажет ровно то, что осталось. Против этого в приложении есть Archive Manager с кнопкой Full Backup — сессии копируются в ~/.claude-history-viewer/archives, куда автоочистка не дотягивается.

Мелочи, на которые я наступил

  • Официальный docker-compose.yml ставит лимит памяти 512 МиБ. На моих 8,4 ГБ истории контейнеру не зватает памяти, так что лимит лучше поднять сразу.

  • Первый скан 8,4 ГБ занял около несколько минут, повторные запросы отдаются из кеша за 0,06 с.

  • 30 дней retention по умолчанию — это меньше, чем кажется, рекомендую в настройках поменять на побольше, полезно для анализа и улучшения флоу работы.

  • Вкладка Recent Edits собирается из транскриптов и в контейнере с одним примонтированным ~/.claude работает. Рабочие каталоги нужны git-функциям, и для них в образ придётся добавить пакет git.

  • В server mode интерфейс дёргает get_startup_session_hint и получает 405 — красная строка в консоли браузера, работе не мешает.

  • Наружу без токена не выставляйте: транскрипты лежат в открытом виде, и всё, что прочитал Read или напечатал Bash, читается в интерфейсе как есть. Для публичного хостинга в 1.14 добавили вход по аккаунту (Argon2id + серверные сессии), read-only-режим и работу за реверс-прокси на подпути.