npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

agentic-screencast

v2.0.0

Published

A video-building CLI for AI agents: declarative scenarios, narration, reproducible frames and MP4 output.

Downloads

987

Readme

Agentic Screencast — инструмент агента для сборки видео

English | Русский

Лицензия: GPL-3.0-or-later (см. LICENSE). Так выбрано потому, что ffmpeg-static поставляется зависимостью и распространяется под GPL-3.0; лицензия слабее создала бы расхождение между файлом лицензии и тем, что пользователь получает при установке.

Агент изучает продукт, прототип, идею или исследование, пишет сценарий и собирает видеопрезентацию для человека. Agentic Screencast выполняет сборку: создаёт слайды, синтезирует речь или берёт записи, рассчитывает кадры и склеивает MP4. Подходит для объяснения продукта, обзора функции, объяснения идеи и презентации с озвучкой. За факты, текст и проверку результата отвечает агент; входную схему и отчёты сборки он получает в машинно читаемом виде.

Готовые страницы рендерятся в назначенные моменты, а живые действия Playwright записываются в WebM вместе с курсором и откликом клика. Неизменные сцены сборки берутся из кэша.

Поручить агенту

Достаточно назвать продукт или функцию: агент найдёт доступные источники, определит сюжет и спросит только о недостающем доступе или существенном выборе. Начать можно со скилла, готового примера и точки входа агента. Сначала соберите черновик движком stub: он создаёт тишину и не обращается к платному сервису.

Работайте через скилл: в нём путь от просьбы до проверенного ролика, бриф, который агент собирает, и база знаний, которую он читает. Claude Code и Codex ставят скиллы сами, поэтому просто попросите агента, например: «Поставь скилл Agentic Screencast из skills/agentic-screencast этого репозитория и сними по нему минутный ролик о нашем экспорте».

Универсальное ядро живёт в src/. Сценарии, снимки, записи и команды конкретного ролика принадлежат внешнему проекту-потребителю; этот репозиторий их не хранит.

Запись настоящих действий

Модуль agentic-screencast/capture снимает настоящие действия в браузере: recordTake открывает новую страницу Playwright, capturePage пишет уже открытую. Методы, опции и масштаб записи — agentic-screencast help capture.

import { recordTake } from "agentic-screencast/capture";

await recordTake({
  output: "captures/run.webm",
  prepare: async (page) => { await page.goto("https://your-app.example/run"); },
}, async (take) => {
  await take.click(take.page.getByRole("tab", { name: "Graph" }));
  await take.withFocusCard(take.page.getByRole("slider"),
    { title: "Перематываем историю", body: "Экран показывает другое состояние запуска.",
      reveal: "type", motion: "glide" },
    async () => { await take.range(take.page.getByRole("slider"), 0.75); });
});

Готовый WebM укажите в file: видеосцены и соберите MP4 обычной командой. Запускаемый пример работает без внешнего сайта. Краткая справка: agentic-screencast help capture.

Рядом с WebM съёмка пишет <дубль>.marks.json: именованные моменты, прямоугольники отмеченных элементов и cameraMoves — интервалы уже снятого приближения и возврата камеры. Сценарий ссылается на момент или элемент через @имя; lint учитывает движение камеры, когда ищет необъяснённую смену экрана внутри дубля. Файл отметок называет и скрипт, записавший дубль. Камеру над таким дублем в видеосцене исполняет браузер: сборка заново запускает этот скрипт с камерой сцены, привязанной к отметкам дубля, и берёт пересъёмку <дубль>.<сцена>.cam.webm, поэтому мелкий текст в наезде остаётся чётким; неизменная камера дубль не переснимает. Условия и запасной путь — в help capture.

Переменные окружения

| Переменная | Что задаёт | Умолчание | |---|---|---| | AGENTIC_SCREENCAST_HOME | каталог данных: кэш звука и кадров, записи голоса, вывод по умолчанию | ./.agentic-screencast | | FFMPEG | путь к ffmpeg, если не устраивает поставляемый зависимостью | из node_modules | | CLOUD_KEY | ключ Yandex SpeechKit; читается из окружения или из .env | — | | AGENTIC_SCREENCAST_LANG | язык, на котором инструмент говорит с человеком | из LANG, иначе en | | LANG | системный язык; берётся, если AGENTIC_SCREENCAST_LANG не задан | — | | AGENTIC_SCREENCAST_FILM_LANG | вариант сценария на другом языке; его ставит --lang ru у команды | язык шапки сценария | | AGENTIC_SCREENCAST_FILM_FORMAT | формат сборки горизонтального сценария с кадрированием; его ставит --format vertical у команды | формат шапки сценария | | AGENTIC_SCREENCAST_NO_LIVE_CAMERA | 1 — камера над живым дублем растягивает видео, а не переснимает дубль с наездом браузером | наезд исполняет браузер | | AGENTIC_SCREENCAST_TAKE_CAMERA | ставит сборка, а не человек: план камеры, который перезапущенный скрипт дубля исполняет в браузере (какой дубль, куда писать, движения с привязкой к отметкам) | не задана: скрипт снимает как обычно | | NODE_TEST_CONTEXT | ставит node --test; дубль, снятый под ним, не записывает свой скрипт, чтобы сборка не перезапускала файл теста | не задана | | AGENTIC_SCREENCAST_BARE | 1 — сборка без слоя поверх материала (подсветки, подписей, карточек): так проверка сравнивает кадрирование самого материала | слой рисуется | | AGENTIC_SCREENCAST_JOBS | сколько сцен рисуется одновременно; каждая — своим браузером от первого кадра до последнего, но при общей нагрузке растеризатор Chromium может округлить точку иначе (PSNR не ниже 56 дБ против сборки по одной), поэтому побайтовая пересборка — с 1; поле отчёта timing показывает, где ушло время | треть ядер, не больше четырёх | | AGENTIC_SCREENCAST_DEBUG | 1 — отказ сборки печатается со стеком Node; без неё — одной строкой build failed: …, которая называет сцену и причину | одна строка | | AGENTIC_SCREENCAST_EMOJI_SET | каталог набора картинок эмодзи вместо поставляемого assets/emoji (Noto Emoji); пустой каталог заставляет сборку отказать на первом эмодзи | assets/emoji пакета |

Имя переменной ключа (CLOUD_KEY) переопределяется данными: key_env у голоса. Съёмка снимков своих переменных не имеет вовсе: имена называет настройка потребителя строками вида ${ИМЯ}. Своих переменных у внешнего движка голоса может быть сколько угодно — он их и объявляет; инструмент о них не знает.

Условие воспроизводимости кадра

Побайтовое совпадение кадров гарантируется на закреплённой версии браузера: путь к бинарю Chromium входит в ключ сборки. Обновление playwright законно разведёт ключи всех сцен: условия воспроизводимости изменились. Версии закреплены и манифестом, и файлом блокировки (package-lock.json), поэтому установка воспроизводима; расхождение возможно только при осознанном обновлении playwright.

Установка

Пользователю инструмента хватает глобальной установки из npm (npm install --global agentic-screencast, затем npx playwright install chromium) — так, как описано в README.md. Этот раздел — для работы из исходников: разработки инструмента или запуска чекаута без установки.

Нужен Node.js 20+. Питона на рабочем пути нет: синтез делает движок голоса, а поставляемые движки работают на узле. Chromium устанавливается отдельной командой Playwright; в Linux ему могут понадобиться системные пакеты.

Исходники — TypeScript в src/, запускается собранное в dist/. Собирать руками не нужно: сборка навешена на prepare, то есть выполняется при npm install в каталоге пакета и при установке из git-адреса.

# 1. зависимости узла и браузер; сборка запускается сама
npm install                                # ставит зависимости и собирает dist/
npx playwright install chromium            # ~180 МБ

# 2. сказать инструменту, где держать кэш и вывод
export AGENTIC_SCREENCAST_HOME="$PWD/.agentic-screencast"

# 3. команда agentic-screencast в PATH — для агентов и оболочки
npm link

Без npm link у чекаута нет команды agentic-screencast, и агент, идущий по скиллу, её не найдёт; тот же CLI запускается как node dist/agentic-screencast.js.

Локальный бесплатный синтез (Silero) и шлюз разборчивости живут отдельно — они держатся за torch, а это окружение примерно на 600 МБ. Ставить их нужно только тому, кому они нужны: external/README.md.

Запуск

Три способа, все равноправные:

node /путь/к/agentic-screencast/dist/agentic-screencast.js build --source story.md
npx agentic-screencast build --source story.md
agentic-screencast build --source story.md

Право на исполнение у собранной точки входа ставит сборка, поэтому символической ссылке не нужно ничего доделывать руками. Каталог данных считается от ТЕКУЩЕГО рабочего каталога, поэтому вызов из своего проекта ничего не пишет в каталог инструмента.

npx agentic-screencast build --source story.md --out pitch.mp4

Одна команда: породит слайды из сценария, синтезирует реплики, отрендерит сцены и склеит видео. Повторный запуск берёт неизменившиеся сцены из кэша.

Путь --out считается от текущего каталога; без него файл ложится в каталог данных ($AGENTIC_SCREENCAST_HOME/pitch.mp4). Ход сборки идёт в поток ошибок построчно — сцена за сценой, — а в стандартный вывод уходит отчёт одним JSON.

Тот же отчёт сохраняется рядом с MP4 как <ролик>.report.json. В audit.expected находятся расчётная длина в кадрах и явно заданные имена частей part:; в audit.measured — число декодированных кадров, длительности готовых потоков видео и звука, наличие дорожек и главы из WebVTT. audit.issues называет рассинхрон, недостающую дорожку или расхождение глав вместе с измеренными значениями; те же находки попадают в warnings и поток ошибок. Каждая запись warnings — и каждая находка lint — имеет вид {rule, id, message, hint}: id называет проверку, rule — правило базы за ней (FC-58 — правило 58 из docs/film-craft.md, VA-5 — шаг 5 раздела о лицензиях в docs/visual-assets.md), hint — что поменять. Пустой список означает совпадение этих свойств готового файла с учётом одного видеокадра и заполнения AAC, но не заменяет просмотр ролика и проверку подписей. У ролика без частей нет файла глав; пересборка без них удаляет старый файл рядом с тем же выходом.

Пока подбираете сцену, собирайте её одну:

npx agentic-screencast build --source story.md --only e1 --out проба.mp4

Полный ролик из двух десятков сцен строится минутами, одна — секундами, и сегмент получается тот же самый, что войдёт в готовый файл.

Кадр без синтеза речи удобно смотреть командой frames:

npx agentic-screencast frames --source story.md --scene e1 --out e1.png
npx agentic-screencast frames --source story.md --except e1 --out sheet.png

--scene порождает только выбранную сцену; --except делает лист остальных, поэтому материал исключённой сцены может ещё отсутствовать. Вместе их не задают. Моменты тактов здесь оценочные. Кадр видео содержит его субтитры и накладку, если у сцены есть речь или overlay. Лист сообщает о большой ровной пустой полосе на нарисованной сцене; намеренная разреженная карточка трейлера не считается ошибкой.

Как устроен вход

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

Сцена — блок: заголовок с идентификатором и видом, несколько полей «ключ: значение», затем речь обычными абзацами без кавычек. Её может подготовить агент или человек, а движок получает текст для озвучки.

Речь сцены делится на такты: абзац — один такт. У такта своя запись, своя длина и свой ключ кэша; длительность сцены — сумма тактов плюс хвост. Перезапись одного такта не трогает соседние. Строка, начинающаяся с ~, задаёт произносимый вариант своего такта: на экране и перед чтецом остаётся обычный текст, а синтезу и адресации записи достаётся вариант.

# Заголовок ролика
voice: {"engine":"speechkit","name":"kuznetsov","speed":1.2}

## s03 · slides.compare
kicker: что это
title: Заголовок сцены
left: Что делает :: выдаёт по одному шагу | не пускает дальше
right: Чего не делает :: не решает задачу
at: 0.7 2.2

Первый такт: он записывается и озвучивается отдельно.

Второй такт той же сцены.
~ Второй такт той же сцены, записанный так, как его надо произнести.

## s04 · slides.chapter
title: Следующий шаг
body: Покажите результат

Теперь перейдём к результату.

Вид сцены принадлежит ПОСТАВЩИКУ материала, а не инструменту. В заголовке сцены до точки стоит поставщик, после — его вид: ## s03 · slides.compare. Поставщик с единственным видом пишется без точки: ## s09 · page.

Инструмент приносит с собой трёх поставщиков:

| Вид | Для чего | Обязательные поля | |---|---|---| | slides.chapter | анимированное вступление или смысловая глава | title, body | | slides.compare | сравнение «без / с» в две колонки | left, right | | slides.chain | схема со стрелками и подписью возврата | nodes | | slides.number | крупная величина с подписью | values либо value | | slides.quote | дословная цитата чужого ответа: крупная кавычка, источник подписью, слова набираются | parts | | slides.hero | открывающее заявление: заголовок встаёт по словам, можно картинку фоном или залить ею буквы (fill) | title | | slides.steps | шаги, загорающиеся по одному вслед за речью | items | | slides.features | сетка из двух–шести возможностей со значками | items | | slides.timeline | вехи на линии, которая прочерчивается к каждой, впереди линии — светящаяся голова | items | | slides.counter | числа, докручивающиеся счётчиком, доли — кольцом, под каждым — маленький график хода (spark) | values либо value | | slides.beforeafter | два состояния интерфейса в одном кадре: слева «было», справа «стало», разделитель едет | image и after | | slides.perspective | снимок на экране в перспективе (WebGL): облёт камерой, блик, отражение | image | | slides.parallax | снимок расслаивается на панели на разной глубине, ближние плывут сильнее | image и panels | | slides.chart | график из CSV «подпись,значение»: столбцы растут, линия прорисовывается, пик подсвечен; type: race — гонка столбцов по периодам CSV | data | | slides.code | код, набирающийся с подсветкой; строкой сценария или файлом | code либо file | | slides.photo | картинка с медленным наездом к точке и подписью | image | | slides.shot | снимок экрана в рамке браузера или телефона, плывущей в 3D | image | | slides.outro | финальная карточка: заголовок, строка, призыв, адрес | title | | slides.card | карточка трейлера: одно–три слова во весь кадр, влетают со вспышкой и тряской | title | | slides.titlecard | название фильма разреженными заглавными с бликом и засветкой | title | | slides.marquee | бесконечная лента логотипов или подписей, одна или две строки навстречу | items | | slides.stack | стопка карточек: на каждом пункте верхняя улетает, колода подъезжает | items | | slides.orbit | иконки на орбитах вокруг заголовка | items | | slides.chat | реплики чата по очереди, перед ответом по строке «Думаю…» и полосам скелетона бежит блик | items | | slides.carousel | кольцо карточек в настоящем 3D, поворачивается к пункту | items | | slides.globe | глобус из точек на WebGL: дуги из первого города к остальным, пинг и подпись; map: flat раскладывает их на плоской карте | items | | slides.layers | разобранный вид: панели снимка поднимаются на свою глубину, камера наклоняется, к концу слои собираются | image и panels | | slides.bento | бенто-сетка: первая ячейка крупная, последние широкие, чтобы сетка закрылась, остальные малые; каждая входит наклоном из глубины | items | | slides.wall | стена снимков, наклонённая в 3D: колонки едут навстречу друг другу под заголовком | images | | slides.cloud | подписи или значки на вращающейся сфере, ближние крупнее и ярче | items | | slides.shell | работающий терминал: команды набираются после приглашения, крутится спиннер, появляется вывод | items | | page | готовая страница: снимок интерфейса или своя вёрстка | page | | report | страница, которую agentic-report собирает из исходного Markdown заново для каждой сцены, сохраняя идентификаторы блоков и исходник; верхняя панель, ревью и переключатели схемы и темы выключены, если метаданные отчёта сами не называют эти ключи; нужен agentic-report >=0.20.0 в проекте | report | | video | готовый видеофайл вместо нарисованной страницы | file |

У сцены page необязательное pageVertical называет отдельный HTML-файл, свёрстанный для кадра 9:16. При вертикальной сборке, в том числе с build --format vertical, берётся он; в остальных форматах — page. Для английского варианта задайте page.en и pageVertical.en. Без pageVertical горизонтальная страница в вертикальной сборке сохраняет обычное движущееся кадрирование. Выбранная вертикальная страница видна также в frames и на странице записи голоса; отсутствие выбранного файла — ошибка, а не повод взять горизонтальный.

В третьей колонке названы только обязательные поля; остальные допустимые перечисляет agentic-screencast schema. У цепочки это back — подпись стрелки возврата, у величины — label и tags, у сцены-экрана — zoom, spotFrom и focus. Что делает каждое поле слайда — входы, движение кадра, живой фон, рамка устройства, — описывает agentic-screencast help slides; вид плёнки, фирменные цвета и тема части ролика — agentic-screencast help themes. Как эти токены темы соответствуют токенам agentic-report, чтобы ролик и страница отчёта выглядели как одно, — в карте токенов темы.

Пункты items пишутся как у колонок: Заголовок :: пояснение | …, эмодзи в начале пункта становится его значком. Если at не назвал моменты, пункты выходят каждый на своём такте, когда тактов хватает, а иначе — ровным шагом по всей речи сцены. Картинки и код встраиваются в порождённую страницу, поэтому замена файла под тем же именем пересобирает сцену. Плотный slides.steps с шестью–восьмью пунктами раскладывается в две колонки горизонтального кадра и в одну вертикальную последовательность на телефоне. Четыре коротких узла slides.chain стоят в одном ряду горизонтально и друг под другом вертикально. Длинные подписи проверяйте на готовом кадре.

Видеовставка приводится к кадру ролика: масштабируется с сохранением пропорций, дополняется полями до нужного размера и переводится в темп ролика. Речь у неё такая же, как у любой сцены, и длину задаёт она; сцена без речи законна у видео, slides.chapter и page. У обычного видео длину задаёт файл; немой главе и сохранённой странице задайте duration в секундах, чтобы текст успел появиться и остаться читаемым. Короче сцены — достаивается последним кадром, длиннее — обрезается. Аннотации могут продлить сцену, чтобы текст успели прочитать.

Движение поверх настоящего видео

Общее поле overlay работает у video, page и других видов сцен: камера, подсветка, карточки, титры, пометки и лупа. Поле speed видеосцены замедляет отрезок клипа или останавливает время внутри того же плана, freezeAt удерживает один настоящий кадр клипа. Их поля, умолчания и поведение описывают agentic-screencast help overlay и help text; рабочий пример показывает вводную, клип и стоп-кадр в одном сценарии, а самодостаточный пример — сохранённую страницу с наездом по CSS-селектору и надписью «иллюстрация».

Ручной указатель overlay.pointer с click:true рисует волну, но не нажимает кнопку в исходной записи: настоящие действия снимайте через Playwright capture. На сохранённой странице и на слайде накладка может сама ответить за страницу: overlay.actions переключают тумблер, вкладку или меню и переставляют список, а его элементы переезжают на новые места плавно; drag у точки указателя переносит элемент, overlay.thinking кладёт «ИИ думает» на место будущего ответа, а overlay.torch ведёт за указателем круг света (agentic-screencast help overlay). У сцены с freezeAt сборка меряет разброс яркости в каждой области area на замороженном кадре и отказывает, если область почти пустая: в отказе названы сцена и измеренный контраст.

Поле stills: @saved :: проверить результат у видеосцены заказывает контрольный кадр на именованной отметке дубля. Можно также назвать такт (b2+0.3), долю сцены (80%) или шаг (every 1s). Полная сборка пишет кадры в каталог <ролик>.stills/ и сообщает для каждого scene, moment, time в секундах готового ролика, заметку note и путь file; сборка с --only кадров не снимает.

Значения полей пишутся коротким размеченным списком. Разделителей два, и значат они в каждом поле своё — вот все:

| Поле | Как писать | Пример | |---|---|---| | left, right | Заголовок :: пункт \| пункт — заголовок колонки и её содержимое; одно значение без \| становится абзацем. Пометка (bad), (good), (plain) в заголовке задаёт окраску | Что приходит (bad) :: «готово» \| не работает | | nodes | узлы цепочки через \|; пометка (acc) или (bad) красит узел | сам поставил \| сам сделал \| сам объявил (bad) | | values | пары величина :: подпись через \| | 300 :: узлов \| 24 :: действия | | parts | пары подпись :: текст через \|; текст цитаты идёт как есть | ответ :: шаг принят | | tags | простой список через \| | что это \| зачем \| как начать | | back | одна строка: подпись стрелки возврата | никто не держит рамку | | note | одна строка под содержимым: у сравнения, цепочки и цитаты; у числа, счётчика и графика — источник цифр; остальные виды поле отвергают | отрисовка ответа, не съёмка |

at — моменты появления элементов якорями:

| Якорь | Что значит | |---|---| | b2 | начало второго такта речи | | b2.end | конец его речи: начало третьего такта, а у последнего такта — конец речи сцены, без хвоста и перехода | | b2+0.4, b2-0.2 | через 0,4 с после начала такта, за 0,2 с до него | | 40% | доля длительности сцены | | 1.2s | секунды от начала сцены; голое число — тоже секунды |

Без at элементы появляются каждый на своём такте: первый на первом, второй на втором. Элементов у сцены больше, чем полей: шапка (надзаголовок с заголовком) — тоже элемент, и она всегда первая. У compare их три (шапка и две колонки), у chain — шапка, каждый узел и подпись возврата, у number — шапка, каждая величина и строка меток, у quote — шапка и каждая часть. Якорь, указывающий дальше последнего такта, разбор отвергает и называет число тактов сцены. Это и есть то, ради чего якоря заведены: слайд собирается вслед за речью и тянется вместе с записью любой длины. Момент, записанный секундой, так не умеет — он назначен до записи и о ней ничего не знает: реплика вышла длиннее, и элемент полсцены ждёт; короче — сцена кончилась раньше, чем он проступил.

Те же якоря принимают focus у сцены-экрана (селектор @ якорь), spotFrom, spotlight и at любого элемента накладки overlay ({"at":"b2+0.5",…}; там же — доля сцены 40%). Незнакомое поле — ошибка разбора, а не молчание: опечатка в имени иначе тихо выбросит содержимое слайда. Отсутствие обязательного поля — тоже ошибка разбора, и она называет вид сцены и имя поля.

У сцены page поля target и mustRead разбору не нужны, но без них признак кадра проверять нечего — см. раздел «Снимок интерфейса как подложка».

Базовую длительность определяет речь — сумма длин её тактов плюс хвост, округлённая вверх до целого кадра. Аннотации могут продлить сцену до окончания последней карточки или клика.

Язык и правила чтения — данные ролика

lang: ru
pronounce: "ru-latin"

lang — язык ролика: на нём подписана страница записи, помечены порождённые страницы и говорят отказы этой сборки. Не назван — берётся из AGENTIC_SCREENCAST_LANG, затем из LANG, иначе английский. Инструмент сам по себе языка ролика не знает и не выбирает.

pronounce — правила чтения: текст для озвучки не равен тексту на экране. Один движок читает латиницу сам, другой молча её пропускает; аббревиатуру надо произнести по буквам, а адрес сайта не произносить вовсе. Это свойства языка и предметной области, поэтому они приходят данными — именем поставляемого набора, путём к своему файлу или прямо объектом:

| Набор | Для чего | |---|---| | ru-latin | русская речь, движок латиницу не читает: словарь сокращений плюс побуквенная замена остатка | | ru-abbr | русская речь, движок латиницу читает сам: правятся только аббревиатуры, адреса выбрасываются |

Свой набор — объект из четырёх необязательных частей: say (замены целых слов), translit (побуквенная замена), drop (что выбросить), cleanup (уборка после замен), плюс order (порядок шагов) и caseSensitive. Термины своей области добавляют сюда, а не в инструмент.

Порядок шагов — не мелочь: словарь, применённый раньше выбрасывания адресов, успевает переписать кусок внутри домена, и адрес перестаёт быть адресом — вместо того чтобы исчезнуть, он произносится наполовину переписанным.

Смена правил меняет строку синтеза, а с ней ключ кэша: сменились правила — звук будет синтезирован заново, у сетевого движка за деньги.

Кадр, качество и оформление — данные ролика

# Заголовок ролика
frame: {"width":1080,"height":1920,"fps":30,"scale":1}
encode: {"crf":18,"preset":"veryfast","pix":"yuv420p","audio":"192k"}
theme: calm-paper

| Шапка | Что задаёт | Умолчание | |---|---|---| | frame | ширина и высота кадра, кадры в секунду, scale | 1920×1080, 25, 1 | | encode | качество видео и битрейт звука | crf 18, veryfast, yuv420p, 192k | | theme | имя поставляемой темы или переменные оформления: уходят в корень страницы и в накладку | neutral | | scheme | light или dark: схема всех названных тем ролика; её цвета ролей — из файла палитр, общего с agentic-report | своя у каждой темы | | format | пресет формата: landscape, vertical (1080×1920, 30 кадров, с безопасной зоной площадок), square | — | | look | вид плёнки: грейд, виньетка, зерно, каше — именем или JSON | — |

Список тем с их шрифтами, переменные договора темы и правила частичной правки (theme: {"preset":"noir","--acc":"#7aa2ff"}) печатает agentic-screencast help themes. Незнакомое имя темы и объект без preset, в котором не хватает переменной, — ошибки разбора, которые называют доступное и недостающее. У каждой темы, кроме blockbuster, есть светлая и тёмная схема; сцена называет свою через theme: {"preset":"noir","scheme":"dark"}, а незнакомая схема или схема, которой у темы нет, — ошибка разбора с перечнем схем темы.

Вертикаль и квадрат одной строкой. Шапка format: vertical задаёт кадр 1080×1920, 30 кадров в секунду и безопасную зону площадок, format: square — 1080×1080 и те же 30 кадров; frame по-прежнему перекрывает размер и темп, а зона пересчитывается под кадр. Горизонтальный сценарий собирается вертикальным ключом --format vertical без переписывания. Как раскладывается кадр и что сборка меряет — agentic-screencast help vertical; как сделать такой ролик читаемым на телефоне — руководство по вертикальным роликам.

Остальное описано в справке инструмента, одним местом на каждую тему: заготовки сценария по жанру (new), кадры без сборки (frames), контрольные кадры stills, режиссёрские правила lint, длинные поля в несколько строк и перевод из того же сценария (.ru-поля, блок речи [ru], --lang ru) — agentic-screencast help vertical; пометки от руки, блики, всплески и лупа — help text и help overlay; переходы и поле fade — help transitions; ролик для страницы — help web.

Вертикальный ролик, тридцать кадров в секунду, светлая тема — всё это шапка сценария, а не правка инструмента. Поставляемые слайды рисуются в сетке шириной 1280 и растягиваются под ЛЮБОЙ кадр: кегли и отступы остаются теми же числами, меняется только увеличение.

scale — множитель плотности пикселей, как у экрана с высоким разрешением: страница рисуется в том же размере, но снимается с большим числом точек. Размер готового файла он НЕ меняет — его задают width и height; при scale: 2 кадр 1920×1080 остаётся 1920×1080, просто рисуется вчетверо дольше. Менять его нужно редко. Тема — пары «переменная — значение»; инструмент кладёт их в :root без собственной интерпретации.

Смена кадра или качества обесценивает кэш кадров — так и задумано: собранное прежним размером к новому не подходит.

Порождаемое кладётся рядом с источником — каталог slides/ и файл .generated-pitch.json. Править их руками бессмысленно: следующий запуск перезапишет. Держите их вне системы контроля версий, иначе вторая копия сценария вернётся тем же путём, каким её убирали.

Флаги --deck и --pitch принимают готовые данные в обход сценария — они для чужих наборов, не для своего ролика.

Договор о поставщике материала

Чем нарисована сцена — решает поставщик, а не инструмент. Ядро не знает ни одного вида сцены: оно спрашивает у того, кого назвал заголовок, какие у него виды и какие у них поля, и просит породить страницу. Поэтому ролик можно собрать из чего угодно — из своей вёрстки, диаграммы, чужого фреймворка, готового видео, — не трогая инструмент.

Поставщик — обычная программа; реализацию пишут на любом языке, не читая исходников. Объявляется она в шапке ролика:

providers: {"my": "python3 /путь/provider.py"}

Что обязан уметь поставщик

Две подкоманды. Ответ — JSON в стандартный вывод и ничего кроме него; отказ — ненулевой код возврата и причина словами в поток ошибок.

| Подкоманда | Аргументы | Ответ | |---|---|---| | kinds | — | {вид: {about, fields, required, effects?, check?, fileField?, video?, offline?, shown?, staging?}} | | page | --scene-json, --out | {file} — путь к порождённой странице |

  • fields — какие поля допустимы у сцены этого вида, required — группы «хотя бы одно из». По ним ядро отвергает опечатку в имени поля: без этого содержимое сцены тихо выпало бы.
  • effects — умолчания расписания для вида: наезд, пятно, переход. Выбор остаётся за поставщиком, ядро исполняет; сцена вправе перебить своими полями.
  • check — пороги приёмки кадра: по ним проверка судит именно этот материал. Слайд разрежен и его текст читают, экран плотен и от него нужно узнавание — числа у них разные и в инструменте не зашиты.
  • shown и staging — какие поля зритель читает в кадре, а какие управляют показом. По shown правило перевода (lint --lang ru) называет поле, оставшееся без перевода; вид, не разделивший поля, этим правилом не проверяется.
  • fileField — материал берётся готовым файлом, названным этим полем, и порождать нечего. Так устроены page и video.
  • Страница обязана быть чистой функцией времени: моменты появления элементов пишутся в data-at якорями, а разрешает их слой композиции. Слой читает data-at, data-type и data-kinetic только у страницы с меткой <html data-sc-page>; у чужой страницы эти имена значат своё.
  • Страница объявляет число своих элементов расписания атрибутом data-slidecast-elements на теле. По нему проверка порядка отличает «страница отрисовалась не целиком» от «так и задумано»; страница, которая молчит, проверяется только на непустоту.
  • Страница объявляет, что у неё считается содержанием, атрибутом data-slidecast-content — списком селекторов через запятую. Признак кадра требует, чтобы хотя бы одно из объявленного в кадре было; страница, которая молчит, этим признаком не судится вовсе. Проверка не знает ничьей разметки — иначе кадр чужого материала объявлялся бы пустым, сколь угодно осмысленный.

Соответствие проверяется программой, а не на слово:

npx agentic-screencast provider-check 'python3 /путь/provider.py'

Проверки

Сборка проверяет закодированные дорожки и главы, но не судит изображение. Слишком много текста, вылезшая за край строка и нечитаемая подпись — предмет check, и запускать его нужно самому.

npx agentic-screencast check --source story.md   # признак кадра: текст, кегли, графика
npx agentic-screencast order --source story.md   # порядок появления элементов
npx agentic-screencast verify                    # четыре проверки ядра рендера
npx agentic-screencast script --source story.md  # читаемый сценарий в поток вывода
npx agentic-screencast schema                    # описание полей сцены, JSON Schema
npx agentic-screencast scenes --source story.md  # сцены источника как JSON
npx agentic-screencast craft spotlight          # правила режиссуры для решения, с номерами FC
npm test                                     # быстрые проверки без браузера
npm run e2e                                  # полные браузерные и видео проверки; в CI запускаются ночью

handover story.md --film <ролик>.mp4 — ворота перед сдачей ролика: запускает lint, требует непустой MP4 и отчёт полной сборки (собран после последней правки сценария, без проблем аудита и предупреждений), проверяет контрольные кадры и checklist.md и печатает {verdict, checks}, при провале — код 1. Отчёт build --only проверку не проходит.

schema печатает машинно читаемое описание сцены, scenes — сцены разобранного сценария в той же форме. Вместе они дают чужому агенту проверку до сборки: получить схему, получить сцены, прогнать одно через другое любым проверяльщиком JSON Schema (черновик 2020-12). Схема порождается из тех же таблиц, по которым разбор отвергает незнакомое поле и требует обязательное, — второго списка не заводится.

check меряет прямо на кадре, а пороги и признак содержания берёт у поставщика и у самой страницы: что считать годным кадром, знает тот, кто его рисует. У поставляемых слайдов это не больше 220 знаков видимого текста и кегль основного текста не меньше 28 пикселей; сверх порогов проверка требует, чтобы в кадре было объявленное страницей содержание, не осталось неподставленных значений, ни одна строка не ушла за край, страница закрывала собой кадр, а текст читался: отношение контраста к его фактической подложке — не меньше 4,5 у основного текста и трёх у текста размером с заголовок (от 48 точек на кадре 1080), как требует WCAG AA. Свой поставщик объявляет свои числа полем check, и чужой материал судится по ним, а не по слайдовым. Готовый видеоклип признаком кадра не судится: check помечает такую сцену отдельным признаком video и объясняет, что её качество меряется по готовому файлу.

verify проверяет ядро: воспроизводимость (два прогона в разных процессах дают одинаковые кадры), живость (кадры меняются), отсутствие реального времени в кадре и сикабельность (кадр из середины сцены совпадает со сквозным прогоном).

lint предупреждает о верхнем титре поверх интерфейса, субтитрах вместе с индикатором прогресса сверху, слишком быстром наборе текста, отрезке дубля, пересекающем не названную в stills отметку, и резкой смене экрана вне отметок и интервалов cameraMoves. Проверка размещает речевые якоря по оценке; после записи голоса сверяйте готовый ролик.

Договор о движке голоса

Порождение звука вынесено за границу инструмента. Движок — обычная программа, которую инструмент запускает; о её устройстве он ничего не знает и нигде не ветвится по её имени. Поэтому реализацию можно написать на любом языке, не читая исходников инструмента.

Движок называется в данных голоса. Если имя совпало с поставляемым — работает поставляемый; иначе имя считается командой (путь или имя в PATH), и её можно указать вместе с тем, чем запускать.

npx agentic-screencast voices --engine speechkit
npx agentic-screencast build --voice-json '{"engine":"speechkit","name":"kuznetsov","speed":1.2}'
npx agentic-screencast build --voice-json '{"engine":"/путь/python /путь/voice.py","name":"baya","rules":"silero"}'
npx agentic-screencast voice-check

Что обязан уметь движок

Четыре подкоманды. Ответ — JSON в стандартный вывод и ничего кроме него; отказ — ненулевой код возврата и причина словами в поток ошибок.

| Подкоманда | Аргументы | Ответ | |---|---|---| | voices | — | список имён голосов, [] если движок их не перечисляет | | synth | --voice-json, --text, --out | {file, duration, engine, voice} | | probe | --voice-json, --text | {fingerprint} | | paths | — | ресурсы, которые движок разрешает сам |

Требования, которые нельзя нарушить:

  • Звук — WAV 48 кГц моно. Из его длины инструмент выводит длительность сцены; сжатый формат или иная частота дадут неверное число, и картинка разъедется со звуком.
  • Ключ доступа движок берёт из окружения. В данные голоса его класть нельзя: объект голоса целиком уходит в ключ кэша и в отчёт сборки.
  • Отпечаток (probe) — строка, которая меняется тогда и только тогда, когда изменился бы звук при тех же тексте и данных голоса. Синтез отвечает пустой строкой: для него текст и данные определяют звук полностью. Непустой отвечает движок, у которого звук берётся снаружи, — например записанный человеком голос: при перезаписи он даёт другой звук при тех же тексте и данных, и без отпечатка сборка отдала бы прежнюю запись из кэша, выглядя успешной.

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

Соответствие договору проверяется программой, а не на слово: agentic-screencast voice-check <команда> гоняет восемь требований против чужого движка и называет каждое нарушение поимённо.

Поставляемые движки

Какие движки поставляются, что каждому нужно и как выбрать голос, печатает agentic-screencast help voice; список голосов движка — agentic-screencast voices. Системный голос (say) идёт в ролик только с явного разрешения владельца — раздел «Voice» скилла.

Ролик, озвученный своим голосом

Движок recorded реализует тот же договор, что и синтез, поэтому собирается ролик теми же командами и с тем же кэшем:

npx agentic-screencast build --source story.md --voice-json '{"engine":"recorded","dir":"/путь/к/записям"}'

Запись адресуется текстом такта: имя файла — md5 от текста плюс .wav. Отсюда следствие, о котором нужно знать заранее: два такта с дословно одинаковым текстом получат одну и ту же запись. Для сценария ролика это верно — одинаковый текст и звучать должен одинаково.

Нужного файла нет — сборка отказывает и называет, какой реплики не хватает, куда её класть и под каким именем. Гадать правило адресации не приходится. Файл на месте, но не читается или повреждён — тоже отказ с именем файла и причиной одной строкой, а не падение посреди рендера.

Каталог задаётся полем dir в данных голоса; без него берётся $AGENTIC_SCREENCAST_HOME/recordings. Формат записи приводится к требуемому договором (WAV 48 кГц моно) самим движком, так что класть можно то, что отдал диктофон.

Правила чтения (rules) с этим движком несовместимы, и он отказывается работать, когда они заданы: правила переписывают текст для синтеза, а человек читает вслух саму реплику — адрес записи считался бы от того, чего он не произносил.

Перезапись доезжает до готового файла. Отпечаток этого движка — хеш содержимого записи, поэтому замена файла меняет ключ кэша, и сборка берёт новый звук с новой длительностью сцены. Отпечаток, посчитанный от имени файла или от текста, выглядел бы работающим и этого не давал: сборка отдала бы прежнюю запись, выглядя успешной.

Чем озвучена каждая сцена, видно в отчёте ключей — поле voiced:

npx agentic-screencast build --source story.md --keys-only

Записать озвучку голосом

Класть файлы в каталог руками не обязательно: инструмент поднимает страницу, на которой видно сцену и её реплику.

npx agentic-screencast record --source story.md

Команда печатает адрес вида http://127.0.0.1:<порт>/ — откройте его в браузере. По каждой сцене показаны картинка, которую увидит зритель, и её речь тактами: у каждого своя строка для чтения вслух, свой ориентир длины и свои кнопки. Записанное можно переслушать, перезаписать или удалить, и перезапись одного такта не трогает соседние. Выбрать вход можно тут же — список микрофонов рядом с кнопкой разрешения; названия устройств браузер показывает только после разрешения, поэтому его и просят заранее.

Страница открывается, даже если дубль видео, файл отметок или материал другой сцены ещё не создан. Доступные сцены можно записывать сразу; отдельный список называет пропущенные сцены, недостающие файлы и нужное действие. Обычная сборка остаётся строгой и требует все материалы.

Пока идёт запись, переключиться на другую сцену нельзя — метки сцен и кнопки перехода заперты: записываемая реплика замкнута на сцену, выбранную при нажатии, и переход посреди чтения означал бы звук не в той сцене, поверх уже записанного там.

Собрать и посмотреть. Кнопка над списком тактов собирает показанную сцену и показывает её тут же, в проигрывателе: тем же ядром, той же озвучкой и тем же расписанием, что и готовый ролик, — но одну сцену, поэтому это секунды, а не минуты. Платного синтеза не бывает: звук берётся из записей. Та же сборка — build --only (раздел «Запуск»).

Закончив, остановите интерфейс — Ctrl+C: сервер закрывается, временные картинки убираются, а записи остаются в хранилище и переживают и остановку, и закрытие вкладки. Дальше ролик собирается обычной командой сборки.

Почему сервер, а не файл на диске. Микрофон браузер даёт только в защищённом контексте: http://127.0.0.1 в него входит, file:// — нет, и там navigator.mediaDevices попросту отсутствует. Слушает сервер только петлю — наружу интерфейс не смотрит.

Ориентир длины — это оценка, а не длительность. Настоящую длину даёт только звук: после записи ориентир сменяется измеренной длительностью, и ровно её сборка берёт за длину сцены.

Каталог записей — тот же, из которого читает движок recorded, и адрес файла считается тем же кодом. Браузер отдаёт сжатый поток, но в хранилище кладётся договорный WAV 48 кГц моно: заглянувший в каталог человек найдёт там звук, а не контейнер браузера.

Локальный бесплатный синтез Silero поставляется внешней реализацией (external/silero/): он держится за torch, а это окружение примерно на 600 МБ, и платить их должен только тот, кому он нужен.

Правила чтения текста для синтеза объявлены в src/speech.ts: Silero молча пропускает латиницу, поэтому для него она переписывается кириллицей, а SpeechKit читает её сам. На экране в обоих случаях остаётся исходное написание.

Выбираются они по имени, названному данными голоса. У поставляемого движка это его имя; у посторонней реализации имя — путь к программе, и правил по нему не найти, поэтому данные голоса могут назвать их прямо:

{"engine": "/путь/python /путь/voice.py", "name": "baya", "rules": "silero"}

Смена движка, голоса или темпа обесценивает кэш и звука, и кадров — так и задумано: длительность сцены выводится из длины реплики.

Темп cps у чернового stub — только оценка. Для точного черновика озвучьте одну сцену окончательным голосом через build --only <сцена> --keys-only --voice-json '<голос>', разделите число произносимых знаков на длительность её тактов spoken в отчёте и задайте полученное cps. Записанный звук уже лежит в кэше и повторно оплачиваться не будет.

Ключ доступа к SpeechKit

Ключ читается из окружения (CLOUD_KEY), а если его там нет — из .env в корне проекта.

Где его взять:

  1. В консоли Yandex Cloud создайте сервисный аккаунт в нужном каталоге.
  2. Выдайте ему роль ai.speechkit-tts.user (или выше).
  3. Создайте для этого аккаунта API-ключ — это и есть CLOUD_KEY.
  4. Идентификатор каталога не нужен: при авторизации API-ключом сервис использует каталог сервисного аккаунта (проверено живым вызовом).

Голос kuznetsov в таблице голосов документации не значится, но API его принимает. Полный список сверен живыми вызовами и лежит в поставляемом движке.

Снимок интерфейса как подложка

npx agentic-screencast snapshot <файл-настройки.json> [каталог-подложек]

Страница работающего приложения сохраняется отладочным протоколом Chromium в MHTML и дальше используется как обычная подложка сцены. Так сделано потому, что интерфейс рисуется на клиенте: сохранённая обычным способом страница при открытии из файла выполнит скрипт заново, запросы уйдут в никуда и в кадре будет пустой экран.

Договор подложки. Инструменту нужен один файл на сцену — самодостаточная страница, скрипты которой при открытии не исполняются (этому отвечает MHTML). Всё остальное — дело потребителя: снимайте подкомандой ниже, своим сценарием Playwright или чем угодно ещё. Файл, отвечающий договору, годится сцене вида page без участия инструмента. Следствие, о котором нужно знать: слой композиции инструмент внедряет до документа (addInitScript), а не тегом <script>.

Подкоманда ничего не знает о вашем приложении. Как войти, какие куки поставить, чего дождаться, что нажать и подвинуть — приходит настройкой:

{
  "base": "http://localhost:8080",
  "width": 1920,
  "height": 1080,
  "signIn": {
    "url": "/api/auth/sign-in/email",
    "json": { "email": "${APP_USER}", "password": "${APP_PASS}" },
    "cookies": ["session_token"]
  },
  "cookies": [{ "name": "beta-accepted", "value": "true" }],
  "screens": [
    { "id": "board", "path": "/board", "await": "[data-testid=card]",
      "prep": [{ "zoom": 1.45, "pause": 600 }], "settle": 1500 }
  ]
}

| Поле | Что задаёт | |---|---| | signIn | запрос входа целиком: url (свой или относительный к base), method, headers, json. Куки ответа переносятся в браузер; cookies называет нужные поимённо, без него берутся все | | cookies | куки, которые приложение ждёт помимо сессии: согласия, режимы, язык | | screens[].id | имя файла подложки; должно быть единственным — настройка с двумя одинаковыми отвергается, иначе второй снимок затёр бы первый | | screens[].await | селектор данных: ожидание по времени может поймать индикатор загрузки, а проверка «кадр не пустой» окажется зелёной на спиннере | | prep | что сделать в живом приложении до снимка: click (сколько раз — times), zoom, drag, inject (css, js), wait (пауза в миллисекундах) | | prep[].pause | сколько ждать после приёма: после click — 120 мс по умолчанию, после zoom — 400; у inject своя пауза, inject.pause, по умолчанию 300 | | screens[].settle | пауза перед самим снимком, миллисекунды: время дорисовать то, чего не выразить селектором ожидания, — вроде раскладки графа или анимации, доигрывающей после появления данных | | drag | привести элемент to в середину кадра, таща поверхность surface; avoid перечисляет то, за что хвататься нельзя, — элементы, которые тащатся сами |

Публичные страницы входа не требуют — тогда signIn просто не объявлен, и снимок берётся как у гостя, то есть ровно так, как страницу увидит посторонний.

Строка вида ${ИМЯ} в любом месте настройки подставляется из окружения или из .env проекта. Так учётные данные не лежат в файле настройки и не попадают в снимок: в MHTML пишется разметка, куки туда не записываются.

Увеличение делается в живом интерфейсе (prep), а не растягиванием снимка: служебные подписи интерфейса — 10–12 пикселей, и в кадре 1920×1080 их иначе не прочесть.

Сцена готовой страницы помечается видом page и объявляет mustRead — селектор того, что обещает показать реплика. Кадр признаётся годным, когда выполнено всё перечисленное:

  • mustRead найден, помещается в кадр целиком и читаем — кегль не меньше 16 экранных пикселей с учётом всех увеличений;
  • цель не вылезает за кадр (доля кадра, занятая целью, не больше 1,15);
  • сама страница закрывает собой не меньше 0,85 кадра — иначе она уехала углом внутрь кадра, и зритель видит подсвеченный угол вместо страницы;
  • цель занимает не меньше 0,002 кадра — вырожденную в точку цель показывать нечем;
  • если увеличение больше единицы, цель без увеличения занимает меньше половины кадра: иначе целью объявили бы весь макет и приближать нечего. При масштабе единица это правило не применяется — честный кадр «страница целиком» им запрещаться не должен.

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

Чего инструмент не делает

Не решает за автора, какой сюжет, смысл действия, факт или голос показывать: живые действия записываются, но проверить, что зритель их понял, должен агент.