Среда, утро. Открываю мессенджер, читаю: «Слушай, а что вчера делал по платформе клиента, есть итог?». Сажусь, смотрю в потолок. Помню что в принципе ковырял миграцию. Помню что был коммит, два или три. Что-то делал по серверу. Кажется поговорил с поддержкой. Деталей — ноль.

И не в первый раз.

У меня компания, я веду 5-7 направлений одновременно. Мне нужно держать в голове куда движется каждое. Раньше я писал сводку дня сам, садился в 22:00, пытался вспомнить. Получалось среднестатистическое: «работал над X, исправил Y, начал Z». Сухо и без деталей. И сам же через неделю не понимал, что имелось в виду.

В одно воскресенье я не выдержал и сел писать скрипт.

Цель простая. В 22:00 каждый день на мой Яндекс-диск падает PDF с тем, что я сегодня делал. Я бегу глазами, при необходимости корректирую. Если работал прямо до полуночи — перезапустил руками. Прошло три с половиной месяца. Покажу что под капотом.

Четыре источника, из которых собираю день

Главная идея простая. Если хочешь честный отчёт без выдумок — не доверяй одному источнику. Любой один врёт. Четыре, перепроверяющие друг друга, уже почти не врут.

Первый — ActivityWatch. Это маленькое приложение на маке, фоновое. Логгирует какое окно сейчас активно, и активен ли пользователь по движениям мыши и клавиатуры. Я поставил его автозапуском, дал разрешения системы и забыл.

Тут грабли вылезают сразу. AW логгирует всё время с момента включения, включая ночь, выходные, когда у меня просто залоченный экран и я в спортзале. Если просто взять «сегодня в AW», получится 14 часов работы. А на деле шесть.

Поэтому в моём скрипте окно жёстко режется по часам, 06:00 — 22:00. Через env-переменную SUMMARY_DAY_END_HOUR можно сдвинуть, если работаю позже. И исключаю системные «приложения», которые на самом деле залоченный экран: loginwindow, screensaver, windowserver. Без этого «топ-приложений дня» возглавляет окно логина, что мягко говоря смешно.

AW отдаёт два кубика данных: window (что у меня сейчас сверху) и afk (отошёл я или нет). По afk считаю «активное время». Но не строго not-afk: добавляю время, когда я смотрю как ИИ работает. Это формально AFK по мыши, но я же сижу и читаю поток.

Второй — журналы AI-сессий. Claude Code и Codex CLI пишут каждый чат в JSONL-файл с таймстемпами по строкам. Это супер удобно. Можно одну длинную сессию (которая живёт неделями) разрезать по датам и понять что именно я сегодня в ней просил.

В лоб не работает. Сессия Claude может быть открыта три недели подряд, там накопилось 4000 событий, из них сегодняшних 12. Я прохожу по строкам, считаю те, где timestamp начинается с искомой даты. Получаю срез: новая ли это сессия (первая дата = сегодня) или продолжение. Сколько событий за день. Сколько моих сообщений.

Тут отдельно фильтрую служебный мусор: системные сообщения, [Request interrupted], шаблонные «Caveat»-обёртки, всё что начинается с <environment_context>. Иначе счётчик «запросов к ИИ» накручивается на пустом месте.

Скриншоты, которые я скидываю Клоду, тоже считаю как «запрос к ИИ». Раньше не считал, и половина дней выглядела вдвое тише, чем была. Особенно когда отлаживаешь интерфейс — кидаешь скрин с подписью «вот тут перерисуй» и за день таких 30 штук.

Третий — git. Здесь обнаружилось что просто git log --since по локальным репозиториям пропускает кучу. У меня примерно половина проектов коммитится на серверах напрямую, без локальной копии. Например клиентская платформа разворачивается там же где живёт; туда я хожу по ssh.

В скрипте веду список remote-репозиториев. Для каждого делаю ssh host 'git -C path log --since=... --pretty=%s'. С BatchMode=yes (не запрашивать пароль интерактивно) и ConnectTimeout=8. Если сервер недоступен — молча пропускаю, не валю весь сбор данных.

За один из дней июня у меня вышло 27 коммитов в трёх репах. Из них 9 локально, 18 на двух серверах. Без серверной части карточка «git-коммитов» врала «работал в одном проекте». А я работал в трёх.

Четвёртый — файлы, изменённые сегодня. Это про доказательство работы там, где не было ни коммита, ни AI-сессии. Концепция в гугл-доке, экспортированная в Markdown. Мокап, сохранённый локально. Заметки. Сценарии видео.

Беру ~/Projects, рекурсивно прохожу, у каждого файла смотрю mtime, фильтрую по дате. Исключаю мусор: .git, node_modules, venv, .next, dist, .playwright-mcp, .cache. Хожу не глубже 5 уровней папок — нет смысла.

Тут вылезла одна гранёная проблема, о ней дальше отдельно.

Архитектура

Собрано в два питон-файла. Первый, collect_day.py, ничего не выдумывает. Он собирает сырые данные и выводит большой JSON в stdout: дату, имя устройства, блок с метриками времени из ActivityWatch (активное время, время с ИИ, процент, топ-приложения), счётчики по AI-сессиям (сколько новых, сколько продолженных, сколько моих сообщений), массив самих сессий с темами и сегодняшними запросами, словарь git-коммитов сгруппированный по проектам (и локальным, и серверным), список today-файлов. Один JSON, всё в нём, дальше его едят.

Второй файл, make_summary.py, читает этот JSON и просит LLM написать содержательную часть. У меня там Gemini 2.5 Flash через OpenRouter. Для этого жанра дёшево и достаточно. Промпт строгий: верни JSON с полями focus, done[{project,items}], observation, wins, friction, sync. Никакого markdown в обёртке.

Метрики (часы, проценты, цифры в карточках) считаю детерминированно из JSON, мимо LLM. ИИ занимается только описательной частью. Это важно. Цифры должны совпадать с тем что в данных, ни одного знака не должна модель «уточнить от себя».

Дальше из всего собирается HTML по шаблону. Брендирование в стиле компании: синий акцент, имя и дата в шапке, проекты карточками с боковой полосой, топ-приложения чипами. Внизу три блока: «Что получается / Что сложно / Что попробовать иначе». Каждая страница на тонком фоне-узоре с нейро-узлами и кодовыми скобками — чисто для бренда, читать не мешает.

HTML рендерю в PDF через headless Chrome и кладу в папку на Яндекс-диске. Имя файла по дате — открыл папку, видишь хронологию.

Раз в день, в 22:00, всё это запускает launchd. Plist лежит в ~/Library/LaunchAgents/, регистрирую через launchctl load. Cron на современном маке штука хрупкая, launchd надёжнее. Если работаю до часу ночи — просто перезапускаю команду руками, PDF перепишется поверх.

Три ляпа, на которых научился

Без них не обошлось. Расскажу как поймал.

Первый. Клон чужого репо считался как моя работа. Я клонировал репозиторий проекта на 350 мегабайт, чтобы изучить структуру. У всех файлов в этот момент mtime стал сегодняшний. Сборщик честно увидел 3400 файлов «изменённых сегодня в этом проекте» и LLM написал что я этот проект «полностью переписал».

Очень лестно, но неправда.

Полез чинить. Решение оказалось не таким очевидным как я ждал. Сначала пробовал отрезать файлы по размеру, потом по типу — всё равно проскакивало. В итоге сделал просто: группируем today-файлы по первому компоненту пути (= проекту), считаем количество. Если в одном проекте больше 25 файлов «сегодня» — это не работа, это клон или git pull чьего-то билда. Выкидываем весь проект целиком. У остальных делаем кап в 8 файлов, чтобы один проект не забил список.

Заодно подправил промпт: «клон или pull — это разведка, не разработка; не приписывать действий по таким файлам».

Второй ляп — серверные коммиты. Их я уже описал выше. Проявлялось так: в день, когда я весь день работал над клиентским ботом (на сервере, без локальной ветки), карточка «git-коммитов» показывала «0 в 0 проектах». Сводка получалась пустая. А я работал часов восемь.

Решилось добавлением второй ветки сбора: список remote-репозиториев с ssh-доступом. Поначалу боялся что ssh подвиснет и весь скрипт умрёт. Поэтому BatchMode=yes и ConnectTimeout=8. Если хост недоступен — молча пропускаем и едем дальше.

Третий — залоченный экран в топ-приложениях. Какое-то время в моих PDF гордо красовалось «Top app: loginwindow — 2ч 14м». То есть Mac «работал» 2 часа на экране логина, пока я был на встрече с клиентом.

Технически: AW логгирует и windowserver, и loginwindow. Они не приложения в человеческом смысле, это процессы macOS, и засчитывать их время как работу нельзя. В скрипте простой фильтр: имя приложения, в котором есть loginwindow, screensaver или windowserver — пропускаем. Но я нашёл это далеко не сразу. Недели две ходил, был уверен что у меня просто очень много залогиниваний.

Два трюка которые сильно облегчили жизнь

Первый — кэш текста и ручные инжекторы через переменные окружения.

В первый месяц я часто перегенерировал сводку: то PDF не такой, то надо подкрутить дизайн. И каждый раз LLM писал заново содержательный текст. Иногда чуть-чуть другой формулировкой, иногда — заметно хуже первого варианта.

Сделал так: первый прогон за день сохраняет содержание в .cache/2026-06-17_content.json. При перерисовке текст не перегенерируется, только метрики, цвета, шрифты. Если хочу заново — SUMMARY_REGEN_TEXT=1. Или просто удаляю файл вручную, что психологически приятнее, чем флаги.

И для случаев, когда работа была вне AI/git/файлов — поговорил с заказчиком по телефону, скинул скрин в Telegram, согласовал доступ — добавил env-инжекторы:

SUMMARY_ADD_DONE='[{"project":"Клиент A","items":["согласовали SLA по обращениям"]}]' \
SUMMARY_ADD_AI_MIN=40 \
SUMMARY_ADD_MSGS=15 \
python3 make_summary.py 2026-06-17

Прибавляет минуты к активному времени и к времени с ИИ, добавляет группу в done, перерисовывает PDF. Не лезет в LLM, всё детерминированно. Цифры пересчитываются.

Второй трюк — управленческий слой поверх «что сделано».

Через месяц я понял что «что сделано» — это самая лёгкая часть отчёта. И самая бесполезная. Мне как руководителю интереснее видеть три других вещи: что у меня получается (чтобы закрепить), где трение (что мешает результату) и что попробовать иначе.

Добавил в промпт три новых поля: wins, friction[{signal, why, try}], sync. Friction даю формировать только по наблюдаемым сигналам, а не «работал долго». Долго работал — это не трение, это работа. Сигналы конкретные: много переделок одного места, возвраты к одной проблеме, переключение задач без главного результата, блокер живёт больше суток, задача висит на доступах.

И отдельный файл рядом с PDF, 2026-06-17_Что-попробовать-иначе.md — короткий, на пару абзацев, с четырьмя разделами. Это уже не отчёт, а почти зеркало для самого себя.

Что в итоге получилось

Полный цикл за день съедает минут 30 wall-clock: ActivityWatch собирает фоном, в 22:00 launchd запускает Python, JSON собирается секунд 10, LLM пишет минуту, Chrome рендерит PDF секунд 5. Итог — 5-7 страниц PDF в моей папке.

Я открываю, бегу глазами, если что-то очевидно не так — правлю через env-инжектор. Это занимает минут пять.

Сводка никому не отправляется автоматически, у нас с командой формат проще. Но когда меня спрашивают «что вчера делал по X» — я больше не молчу как студент на сессии. Открываю PDF, читаю, отвечаю по сути за минуту.

И ещё одна штука. Через месяц я начал замечать паттерны. Например, по средам я почему-то стабильно проседаю на «времени с ИИ». Оказалось, в среду у меня встречи. Или что три недели подряд один блокер «жду доступа к 1С» торчит в friction.sync. Пошёл и продавил его наконец.

Сводка сама по себе ничего не решает. Но привычка её перечитывать утром следующего дня — да.

Что собираюсь улучшать

В системе пока нет адекватного учёта телефонных разговоров и встреч. Сейчас я добавляю их руками через SUMMARY_ADD_DONE. Думаю прицепить календарь и распознавание Zoom-сессий из истории — но это уже следующий шаг, пока подождёт.

Ещё хочу научить LLM хорошо отличать «я что-то новое сделал» от «я обсуждал то, что сделал раньше». Сейчас в промпт зашит длинный кусок про этот капкан, помогает но не идеально.

И если у кого-то из читающих есть похожая система — было бы интересно сравнить что вы туда подкладываете. Может я что-то очевидное упускаю. Особенно интересно про календарь и про учёт коммуникаций — у меня ощущение что 4 источника не предел.

Комментарии (1)


  1. ZamirHa
    18.06.2026 13:13

    То есть, когда за вас кто-то собирает информацию и подает вам каждое утро отчет - называется "теперь я все помню"?

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