App Landing SEO — два агентных навыка: SEO-лендинг для мобильного приложения и генерация картинок

· 9 мин чтения
skills seo mobile-development claude-code image-generation
📂 Исходный код на GitHub

Пакет из двух агентных навыков для кодинг-агентов: app-landing-seo собирает SEO-лендинг для мобильного приложения с гайдами и разметкой Schema.org, gpt-image генерирует к нему изображения через OpenAI gpt-image-2. Установка одной командой npx, лицензия MIT.

App Landing SEO — два агентных навыка: SEO-лендинг для мобильного приложения и генерация картинок

У мобильного приложения обычно есть карточка в магазине — и почти всегда нет нормальной страницы в вебе. Поисковики не читают описание из App Store, а значит приложение просто не находят по запросам вроде «как вести учёт остатков». App Landing SEO — это набор из двух агентных навыков, который закрывает эту дыру: один собирает SEO-лендинг с гайдами под ключевые запросы, второй рисует к этим гайдам иллюстрации.

Что внутри пакета

Навык Что делает
app-landing-seo Исследует карточку приложения в магазине, подбирает ключевые запросы, собирает статический лендинг, пишет гайды, добавляет разметку Schema.org и проверяет результат
gpt-image Генерирует и правит растровые изображения через модель OpenAI gpt-image-2

Навык gpt-image лежит в том же репозитории, но умеет работать и сам по себе. Основной навык вызывает его, когда доходит до картинок для гайдов.

Установка

npx github:AustineA/app-landing-seo

Установщик копирует оба навыка в каталог ~/.agents/skills/ и делает символические ссылки в каждом найденном агенте. Список агентов фиксированный:

  • Claude Code
  • Codex
  • Cursor
  • Windsurf
  • Antigravity
  • Trae
  • Amp

Если каталога конкретного агента нет, установщик его пропускает. Ссылка делается один раз, а не копия — обновление файлов в ~/.agents/skills/ сразу видно во всех агентах.

Дополнительные команды:

npx github:AustineA/app-landing-seo status    # что установлено и куда подключено
npx github:AustineA/app-landing-seo uninstall # убрать оба навыка и все ссылки

Сам установщик лежит в bin/install.mjs — это обычный скрипт на Node.js без зависимостей, его легко прочитать перед запуском.

Что нужно из окружения

  • Node.js 18 и новее — для самого установщика
  • uv (предпочтительно) или Python 3 с пакетом openai — для генерации картинок
  • GPT_API_KEY или OPENAI_API_KEY — ключ OpenAI
  • Доступ в интернет — для исследования магазина и для обращения к Images API

Список зависимостей для навыка с картинками лежит в skill/gpt-image/requirements.txt, там ровно одна строка: openai.

Как пользоваться

Откройте проект приложения или лендинга в агенте и скажите одну фразу:

SEO optimize my app landing page

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

Шесть фаз работы

Полное описание сценария лежит в skill/app-landing-seo/SKILL.md. Вот как они устроены.

Фаза 1. Данные из магазина

Агент тянет полные метаданные через публичный API iTunes — авторизация не нужна:

curl -s "https://itunes.apple.com/lookup?id=<APP_ID>&country=us"

Оттуда берутся название, bundleId, описание, цена, жанры, число оценок, скриншоты, иконка, версия и продавец. Навык также проверяет карточку в Google Play — предполагать, что приложение только для iOS, не надо.

Скриншоты и иконку он скачивает в полном разрешении. Для этого достаточно поменять суффикс в ссылке: 512x512bb.jpg → 1024x1024bb.png для иконки, 392x696bb.jpg → 1284x2282bb.png для скриншотов.

Дальше агент читает каждый скриншот и описывает то, что на нём видно. Оттуда же он берёт цвета бренда и стиль шрифта. Всё это ложится в файл app.md в корне проекта: таблица идентичности, дословное описание, список функций, список файлов. Дальше по этому файлу строится вся работа.

Фаза 2. Подбор ключевых запросов

Если подключены MCP-инструменты astro, агент берёт подсказки ключей и текущую позицию приложения в App Store. Параллельно он проверяет длинные запросы через обычный поиск в вебе, потому что лендинг ранжируется в Google, а не в магазине.

Запросы делятся на три группы: 3–5 основных запросов идут в title, H1 и первый экран, дополнительные — в заголовки секций и тексты о функциях, ещё 6–10 длинных запросов — каждый получает отдельную страницу-гайд.

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

Ещё одно правило — смотреть выдачу по каждому основному запросу и отбрасывать отравленные. По запросу «stock tracker» выдача может вернуть приложения для биржи, а не для склада.

Фаза 3. Сборка лендинга

По умолчанию стек — Vite и React со статической генерацией на этапе сборки. Обычная SPA без рендеринга на сервере для поисковиков почти пустая, поэтому на выходе нужен готовый HTML.

Примерно тридцать строк скрипта генерации решают задачу, и клиентский JavaScript на сайт не попадает: аккордеон FAQ работает на нативном <details>.

landing/
  package.json          # build: vite build --ssr ... && node scripts/prerender.mjs
  vite.config.ts        # plugins: [react()], ssr: { noExternal: true }
  index.html            # только для разработки: <div id="root"> + /src/entry-client.tsx
  public/               # style.css, assets/
  src/
    data.ts             # весь текст: FAQ, тела гайдов, заголовки, описания
    seo.ts              # тип PageMeta и сборщики JSON-LD
    components.tsx      # Header, Footer, StoreBadge, FaqItem, GuideCard, CtaCallout
    pages/              # Home.tsx, GuidePage.tsx, GuidesIndex.tsx
    routes.tsx          # ROUTES: по записи на страницу, гайды собираются из data.ts
    entry-server.tsx    # renderRoute(route): полный документ — head, JSON-LD, тело
    entry-client.tsx    # только для разработки
  scripts/prerender.mjs # пишет dist/<path>/index.html + sitemap.xml + robots.txt

Ключевые решения внутри этой схемы:

  • renderToStaticMarkup, а не renderToString — в HTML не остаётся служебных меток гидратации, а она и не нужна
  • Пути заканчиваются на /, скрипт пишет <путь>/index.html, внутренние ссылки абсолютные от корня сайта
  • На проде отдаётся только статика, клиентский вход остаётся для dev-сервера
  • Атрибут в React называется fetchPriority, строчными буквами он ругается предупреждением

Требования к странице:

Элемент Правило
H1 Ровно один, с главным ключевым запросом
title До 60 символов, формат «Бренд — ключевой запрос для аудитории»
Meta description До 155 символов, ключ плюс отличия
JSON-LD SoftwareApplication (с offers и installUrl), FAQPage, WebSite
Скриншоты В alt — что реально на экране; вес до 200 КБ после сжатия
FAQ 8–10 вопросов, каждый — реальный поисковый запрос, ответ в 2–3 предложения

Объекты разметки собираются в seo.ts из data.ts. Благодаря этому текст, который видит человек, и текст, который читает поисковик, всегда совпадают: массив FAQPage и видимый FAQ берутся из одного места.

Фаза 4. Страницы-гайды

На каждый длинный запрос — одна страница. Все гайды описаны как структура данных, а не как готовый HTML:

{ slug, title, description, h1, intro, featureImage: { src, alt, width, height },
  howto?, sections: [{ h2, paras, cta?, figure? }], faq, related }

Из этой структуры получается всё остальное: карточка, блок призыва к действию, ссылки в подвале, запись в sitemap.xml и разметка.

Адрес страницы совпадает с ключевым запросом: /guides/how-to-keep-track-of-inventory-small-business. Первый абзац сразу отвечает на вопрос — это приманка для быстрого ответа в выдаче. Дальше идёт обучение задаче, а приложение упоминается как инструмент. Сначала учим, потом продаём.

Заголовки H2 держат вторичные формулировки того же запроса. Объём — 600–900 слов по делу. На странице нужны скриншот приложения с подписью, призыв к действию в середине и свой FAQ из двух вопросов, которого нет на главной. Разметка — Article, BreadcrumbList, FAQPage, а для пошаговых гайдов ещё и HowTo.

Картинки для гайдов навык не подменяет: ни стоковыми снимками, ни градиентами, ни другими моделями. Он вызывает навык gpt-image, который работает через OpenAI gpt-image-2. Полный сценарий — в skill/gpt-image/SKILL.md, сам генератор лежит в skill/gpt-image/scripts/generate_image.py.

Каждый промпт для картинки заканчивается запретом на любой текст:

Landscape editorial feature image, 3:2 composition. Absolutely no text, words,
letters, numbers, captions, watermarks, interface labels, or logos.

Параметры вызова:

"$GPT_IMAGE_SKILL_DIR/scripts/generate_image.py" "<промпт под гайд>" \
  --model gpt-image-2 \
  --aspect-ratio 3:2 \
  --quality high \
  --background opaque \
  --format webp \
  --output "<каталог сайта>/assets/guides/<slug>-feature.webp"

На выходе картинка 1536×1024. Дальше навык открывает её в полном размере и проверяет: нет ли лишних букв, не поехали ли объекты, не появился ли чужой логотип, не срезается ли кадр на карточке. Если что-то не так, промпт меняют и картинку генерируют заново. Больше двух попыток на один гайд не делают.

У gpt-image есть параметры и для других задач: --sprite для игровых ассетов на прозрачном фоне, --reference для правки по образцу, форматы png, jpeg, webp.

Фаза 5. Чистка текста от ИИ-мусора

Отдельная фаза проходит по всему написанному тексту. Навык смотрит на признаки машинного текста в порядке заметности:

  1. Длинное тире — главный признак. На весь сайт допустимо несколько штук, не по одной на абзац
  2. Отрицательные параллелизмы: «не X, а Y». Максимум один на сайт
  3. Правило тройки: «без A, без B, без C». Разбивать на пары или на обычный список
  4. Одинаковые зачины у всех страниц
  5. Ровный ритм коротких фраз подряд
  6. Словарь запрещённых слов: delve, tapestry, robust, seamless, effortless, leverage, elevate, showcase, streamline и подобные
  7. Обороты «выступает как», «служит как» там, где хватает «является»
  8. Причастные обороты в конце фразы: «подчёркивая», «отражая», «выделяя»

Проверка делается скриптом, а не на глаз:

grep -o "—" <файлы> | wc -l
grep -inE "delve|tapestry|testament|pivotal|crucial|vibrant|seamless|effortless|leverage|elevate|robust|meticulous|showcas|foster|transformativ|game.chang|revolutioniz|empower|streamlin" <файлы>

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

Фаза 6. Проверка

Сборка npm run build должна пройти. После этого навык разбирает собранные файлы dist/**/index.html скриптом и проверяет:

  • ровно один H1 на страницу
  • присутствует canonical
  • каждый блок <script type="application/ld+json"> разбирается
  • каждая внутренняя ссылка и картинка ведёт на существующий файл
  • каждая запись в sitemap.xml соответствует реальному файлу
  • видимые вопросы FAQ на главной совпадают с name в FAQPage

Дальше — отдельные проверки картинок и локальный запуск dist/ через python3 -m http.server. Проверять через file:// нельзя: на таких адресах ломаются абсолютные ссылки, а чистые адреса страниц на них не работают.

Отдельно навык просит сделать скриншоты в headless Chrome на широком и узком экране и посмотреть на них. В репозитории записаны три известные ловушки:

  • Chrome в headless ограничивает ширину окна примерно 500 пикселями, поэтому съёмка с --window-size=390 даст обрезанную вёрстку, а не мобильную версию
  • префлайт Tailwind убирает маркеры у ol и ul, их нужно вернуть в стилях статьи
  • SVG-логотипы без viewBox при изменении размера кадрируются, а не масштабируются

После выкладки навык напоминает отправить sitemap.xml в Google Search Console.

Бонус: ASO

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

Лицензия

MIT, файл лежит в LICENSE. Код на JavaScript, зависимостей нет.

Источник: https://github.com/AustineA/app-landing-seo