Skip to main content
Glama

earmark

Нажмите на элемент в вашем работающем приложении, скажите, что должно измениться, и ваш агент программирования получит CSS-селектор, файл и строку исходного кода, путь к компоненту, вычисленные стили и геометрию блока — вместо «кнопка справа выглядит неправильно».

Работает в любом фреймворке. Для оверлея не требуется шаг сборки.

┌─ browser ──────────────┐        ┌─ broker ────────┐        ┌─ agent ─────────┐
│ click → annotate       │ POST   │ store + SSE     │  MCP   │ list / watch    │
│ pins, panel, markdown  │───────▶│ long-poll       │◀──────▶│ ask / resolve   │
│                        │◀───────│ .earmark/*.json │        │ dismiss         │
└────────────────────────┘  SSE   └─────────────────┘        └─────────────────┘

Попробуйте за 30 секунд

npm install && npm run example

Откройте http://127.0.0.1:5173/examples/vanilla/, нажмите стрелку на панели инструментов (внизу справа) или нажмите alt+a, затем щёлкните любой элемент на странице.

Лендинг и полное руководство обслуживаются рядом по адресу http://127.0.0.1:5173/site/ — исходный код в site/index.html, одном автономном файле без зависимостей.

Для синхронизации с агентом в реальном времени запустите брокер во втором терминале:

npm run server

Related MCP server: vibe-annotations

Установка

npm install -D earmark
import { createEarmark } from 'earmark';

if (import.meta.env.DEV) {
  createEarmark();
}

Без бандлера:

<script type="module" src="/node_modules/earmark/src/index.js" data-earmark-auto></script>

Параметры

createEarmark({
  endpoint: 'http://127.0.0.1:7331', // or false for copy-paste only
  hotkey: 'alt+a',
  theme: 'auto',                     // 'auto' | 'light' | 'dark'
  persist: true,                     // keep annotations across reloads
  onAnnotate: (annotation) => {},
});

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


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

Инструмент

Что делает

➤

Нажмите на элемент. Shift-клик добавляет ещё, затем клик завершает выбор.

T

Выделите текст — точная строка — это самое удобное для поиска, что можно передать агенту.

⛶

Перетащите область. Сообщает обо всех элементах внутри или отмечает пустую область.

❊

Замораживает всё движущееся — CSS-анимации, element.animate(), <video>, <audio>.

☰

Панель: просмотр, удаление, ответ агенту, копирование markdown.

⌘↵ сохраняет аннотацию, esc отменяет, alt+a переключает режим выбора. Каждая аннотация может быть помечена высоким, обычным или низким приоритетом; high сортируется первым для агента.


Режим копирования и вставки

Нажмите Copy markdown на панели и вставьте в своего агента:

## UI feedback — 1 annotation

- **Page:** http://localhost:5173/dashboard
- **Viewport:** 1440×900 @2x, dark mode
- **Framework:** react

### 1. Export button padding is too tight — needs 10px 16px

- **Element:** `<button>` <ExportButton>
- **Selector:** `[data-testid="export-btn"]`
- **Source:** `src/components/Card.tsx:42:7`
- **Component path:** App › Dashboard › Card › ExportButton
- **Text:** "Export"
- **Box:** 66×37 at (194, 376)
- **Computed:** padding: 7px 13px; border-radius: 8px; font-size: 13px
- **Ancestors:** div.row ← section.card ← main

Режим синхронизации с агентом (MCP)

claude mcp add earmark -- npx -y earmark-mcp

Или, чтобы записать его в .mcp.json проекта:

npx earmark-mcp init

Этот процесс запускает MCP-сервер и брокер, с которым общается браузер. Если что-то не работает, спросите его почему:

npx earmark-mcp doctor
✓ Node version: v24.12.0
✓ sqlite backend: available
✓ MCP registration: earmark is registered in .mcp.json
✓ Broker: responding on http://127.0.0.1:7331 — 1 annotations, 2 sessions
✓ Browser overlay: http://localhost:5173/ (1 annotations)

Каждая неудачная проверка выводит команду, которая это исправляет, а doctor завершается с ненулевым кодом, чтобы CI мог его использовать.

Инструменты

Инструмент

Назначение

earmark_list_annotations

Незавершённая работа в виде markdown (или format: "json"); ограничение по session

earmark_watch_annotations

Блокирует выполнение, пока человек не создаст аннотацию

earmark_get_annotation

Одна аннотация с полной веткой ответов

earmark_list_sessions

Какие вкладки браузера открыты и какие маршруты были аннотированы

earmark_get_session

Одна вкладка со всеми созданными ею аннотациями

earmark_acknowledge

«Я прочитал, я занимаюсь этим» — булавка становится синей

earmark_ask

Задать уточняющий вопрос — булавка становится янтарной

earmark_resolve

Отметить выполненным с резюме — булавка становится зелёной

earmark_dismiss

Отклонить с причиной, которую видит человек

earmark_clear

Удалить всё

earmark_status

Подключён ли оверлей? Какую конечную точку он должен использовать?

Цикл исправлений, который это обеспечивает:

watch → acknowledge → read the source path → edit the file → resolve → watch

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

Когда отзыв неоднозначен, используйте ask вместо предположений. Вопрос появляется на булавке; ответ человека пробуждает следующий watch.

Статусы

open → acknowledged → resolved, с needs-input, когда агент ждёт человека, и dismissed, когда он отклоняет. Булавки имеют цветовую кодировку: оранжевый, синий, зелёный, янтарный, серый.

Сессии

Сессия — это одна вкладка браузера, а не одна загрузка страницы — идентификатор хранится в sessionStorage, поэтому он переживает перезагрузки. Аннотации несут собственный page.url, поэтому сессия, прошедшая по трём маршрутам, даёт агенту одну группу с тремя элементами разных маршрутов.

Навигация в SPA тоже отслеживается: pushState, replaceState, popstate и hashchange обновляют список маршрутов сессии. Вкладка считается подключённой ровно столько, сколько открыт её SSE-поток.

curl http://127.0.0.1:7331/sessions

Пути к исходным файлам

Селекторы говорят агенту, что искать с помощью grep. Пути к исходникам говорят ему, где именно искать, а это разница между одной правкой и тремя grep-запросами.

React 19 удалил поле _debugSource в волокне во время выполнения, поэтому это делается на этапе сборки:

// vite.config.js
import earmark from 'vite-plugin-earmark';

export default {
  plugins: [react(), earmark()],
};

Каждый встроенный JSX-элемент получает data-earmark-src="src/Card.tsx:42:7" во время vite dev. Плагин также внедряет оверлей, поэтому createEarmark() в коде приложения становится необязательным.

earmark({
  inject: false,        // do not auto-mount the overlay
  endpoint: '…',        // passed through to createEarmark
  applyInBuild: true,   // also stamp production builds (off by default)
})

Без плагина всё по-прежнему работает — вы получаете селекторы, имена компонентов и текст, только не file:line. Вы также можете добавить data-earmark-src вручную.

Простой HTML и CSS — без шага сборки

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

  • HTML — документ повторно загружается и разбирается с отслеживанием позиций, затем путь по индексам дочерних элементов проходит по исходному коду. Каждый шаг сверяется с актуальным именем тега, поэтому страница, отрендеренная фреймворком (где обслуживаемый HTML — всего лишь оболочка), не сообщает ничего, а не выдумывает строку.

  • CSS — каждое правило, соответствующее элементу, сопоставляется с файлом и строкой, где оно объявлено. Это работает везде, в фреймворке и без него.

- **Source:** `index.html:101:11` _(resolved from the served HTML)_
- **CSS rules that style it:**
  - `button` → `index.html (inline <style>):49`
    - padding: 7px 13px; border-radius: 8px; border: 1px solid var(--line);
  - `button.primary` → `index.html (inline <style>):59`
    - background: var(--accent); color: rgb(255, 255, 255);

Теперь агент знает, что padding, который нужно изменить, находится на строке 49 в общем правиле button, а не в .primary. Встроенные блоки <style> смещаются в свой родительский документ; внешние таблицы стилей сообщают собственный путь; кросс-доменные таблицы стилей пропускаются, поскольку их содержимое недоступно для чтения.


Автономный брокер

npx earmark-server --port 7331
curl http://127.0.0.1:7331/markdown

Маршрут

GET /health

проверка состояния + счётчики

GET /annotations?status=open&session=ID

список

POST /annotations

создание (пакетно)

GET /annotations/wait?since=N&timeout=30000

длинный опрос

PATCH /annotations/:id

обновление статуса

POST /annotations/:id/replies

добавление в ветку

DELETE /annotations/:id · DELETE /annotations

удаление · очистка

POST /session

регистрация вкладки / запись смены маршрута

GET /sessions · GET /sessions/:id

вкладки со счётчиками и аннотациями

GET /events?session=ID

SSE-поток; также сигнал активности вкладки

GET /markdown

документ для агента

Флаги: --host --store --file --no-persist --webhook --token --quiet.

Хранилище

--store json (по умолчанию) записывает читаемый .earmark/annotations.json с задержкой 250 мс. --store sqlite записывает каждое изменение немедленно в .earmark/annotations.db через node:sqlite, поэтому при сбое теряется максимум выполняемый оператор — без зависимостей, Node 22.5+, и он откатывается к JSON, если недоступен. --store memory ничего не сохраняет.

Вебхуки

npx earmark-server --webhook https://hooks.example/earmark

Также EARMARK_WEBHOOK_URL и EARMARK_WEBHOOKS (через запятую). Каждое событие аннотации отправляется методом POST с заголовком x-earmark-event. Доставка выполняется по принципу «отправил и забыл» с таймаутом 5 с и одной повторной попыткой, поэтому неработающая конечная точка не может заблокировать цикл аннотаций.


Безопасность

Это инструмент разработки.

  • Брокер привязывается только к 127.0.0.1. Не привязывайте его к 0.0.0.0.

  • CORS открыт по замыслу — ваш dev-сервер работает на произвольном origin.

  • Любая страница, открытая в вашем браузере, может обратиться к loopback-порту. Передайте --token SECRET, если это важно для вашей машины.

  • Вебхуки отправляют содержимое аннотаций за пределы вашей машины — URL страниц, текст элементов и всё, что вы ввели. Настраивайте только те конечные точки, которыми управляете.

  • Определение исходного кода повторно загружает вашу собственную страницу и таблицы стилей с того же origin. Никуда ничего не отправляется.

  • Не запускайте его на общем или публичном хосте.


Тесты

npm test

Семь наборов, 88 тестов: поведение хранилища и HTTP, поверхность MCP, управляемая реальным stdio-клиентом, синхронизирующий клиент оверлея, оба бэкенда персистентности, доставка вебхуков, CLI init/doctor и резолверы исходного кода.


Не поддерживается

Только настольные браузеры. Никаких iframe, никакого внутреннего устройства canvas/WebGL, никаких скриншотов. Полный открытый список и обоснование каждого дизайн-решения см. в plan.md.


Лицензия

MIT. Реализация в режиме clean-room — не производная от исходного кода какого-либо другого инструмента.

Related MCP Connectors

Related MCP Servers