Задача

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

Из окна чата это не объясняется никак. Разница живёт сразу в четырёх местах, и ни одно из них из сессии не видно:

область установки   скилл глобальный либо установлен только в одну папку
приоритет           два клиента читают один каталог по разным правилам
инструкции          CLAUDE.md называет скилл либо называет несуществующий
каталог запуска     скиллы читаются относительно того, где агент стартовал

Проект в Onplate это папка с кодом на локальной машине. Добавление ничего не устанавливает, не создаёт файлы, не делает коммит и не отправляет данные наружу. Это пометка «я здесь работаю», и всё дальнейшее читается из папки, на которую она указывает.

То, что получит там агент, читается из самой папки и из одного каталога за её пределами:

<project>/
├── CLAUDE.md, AGENTS.md    агент читает это раньше любого скилла
├── .claude/skills/         проектные ресурсы, читаются при старте здесь
├── .agents/skills/         одна копия, её читают и Codex, и Cursor
├── .mcp.json               проектные MCP-серверы
└── .onplate/profile.json   агент это не читает: о чём договорилась команда

~/.claude/skills/           глобальные ресурсы, во всех проектах клиента
        |
        v
что агент получает в этой папке
  всегда в контексте        тратится на каждом запросе
  максимум при активации    добавляется, только если ресурс сработал

Всё, кроме последней строки, относится к этой папке, и ровно поэтому два репозитория на одной машине ведут себя по-разному.

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

Три формы проекта и четвёртый ответ

Каталоги устроены по-разному, и от формы зависит, куда ресурс вообще положить:

Форма Что это Чем отличается
Одна папка с кодом обычный репозиторий одна часть, один профиль
Монорепозиторий один .git, внутри backend, frontend и так далее части перечислены, ветка одна на всех, профиль один
Рабочая папка пустая папка, в которую наклонировано несколько репозиториев у каждой части своя ветка и свой профиль

Есть и четвёртый ответ, и он как раз удивляет: это похоже не на проект, а на папку с проектами. Именно так Onplate отзывается о ~/code, в котором лежит десяток независимых репозиториев. Добавить его одним проектом означает один профиль и одну карточку на всё, что есть на машине.

Форма определяется по фактам, есть ли у части свой .git и какие маркеры оставляет код, а не по названиям папок.

Действие 1: что папка говорит до всякой настройки

Вкладку «Обзор» можно читать на проекте, который добавили минуту назад и ни разу не настраивали. Разделы, которым нечего сказать, складываются в одну строку со своей сводкой: «отслеживаемое состояние проекта не менялось», «совпадает с профилем», «Git на этой машине недоступен». Раздел, который упал или ещё грузится, не складывается никогда, потому что ему как раз есть что сказать.

Верхний блок это состояние настройки, и это список задач, а не оценка:

Состояние Что это значит
Синхронизировано папка, профиль и установки в AI согласованы
Нужны действия есть что посмотреть
Заблокирован папка недоступна, профиль не разбирается или найден конфликт

«Нужны действия» это нормальное состояние живого проекта. У каждой записи есть конкретный следующий шаг («Проверить снова», «Открыть папку», «Создать профиль», «Посмотреть профиль», «Посмотреть локальную настройку») вместо предупреждения, из которого читатель должен догадаться сам.

Одна строка в этом списке весит больше остальных: ресурс, который не вызывался 30 дней, показанный вместе с ценой хранения, в токенах постоянного контекста на каждый запуск агента. В этой паре всё дело. Неиспользуемый скилл это пожатие плечами; неиспользуемый скилл, который стоит токенов на каждом запросе, это решение.

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

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

Команды запуска собираются из docker-compose.yml, Makefile, скриптов пакета и только потом README, в этом порядке надёжности, каждая с назначением и файлом происхождения. Расхождения за читателя не разрешаются: если Makefile говорит одно, а README другое, показаны обе команды, и часто это самый полезный факт на экране, потому что README устарел.

Команду можно скопировать, и её никто не запускает. Рискованные помечены «сканер насторожился» или «сканер против».

Действие 2: что доезжает до модели в этой папке

Блок контекста отвечает на один вопрос для каждого клиента: что модель реально получает, стартуя здесь? Внутри выбранного клиента ответ делится на три части:

инструкции            CLAUDE.md, AGENTS.md и то, что подтягивают их @-импорты
проектные ресурсы     установленные в эту папку
глобальные ресурсы    установленные в клиент, видны во всех проектах

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

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

Два числа в шапке разведены намеренно: их сумма даёт итог, который не наступает никогда.

Деление по происхождению важнее счётчика, и на вкладке «Ресурсы» оно сделано главной осью:

Группа Что это
Установлено через Onplate записи, которые он знает: их можно обновить, заменить, удалить
Приехало вместе с кодом лежит внутри папки проекта, Onplate это не устанавливал, AI читает это так же
Общее для AI-инструмента лежит в домашнем каталоге клиента, действует во всех его проектах

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

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

Проектный ресурс Глобальный ресурс
Где виден только в этой папке во всех проектах этого клиента
Кто решает вы или профиль проекта вы, один раз
Уезжает ли в репозиторий да, если включён в профиль нет, пока не выбран явно

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

Действие 3: почему агент вообще видит этот скилл

Причин, по которым ресурс здесь доступен, четыре, и они названы, а не сложены в сумму:

установлен в папку проекта   лежит внутри этой папки
доступен через AI            глобальный, виден во всех проектах клиента
назван инструкциями          CLAUDE.md упоминает его или тянет через @-импорт
привезён пакетом             установлен вместе с другими, а не отдельно

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

Это карта доступности и заявленных связей. Это не журнал вызовов, и она не утверждает, что один ресурс запускает другой. Фактические вызовы считаются ровно в одном месте, на вкладке «Сессии», и этот блок отправляет туда числом, а не повторяет ту же таблицу под другим заголовком.

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

Действие 4: скиллы читаются относительно каталога запуска

Вот эта поломка тихая, и она стоит дороже всех. В монорепозитории скилл, поставленный в frontend, читается, когда агент стартует в frontend. Стартуем в backend, где работа обычно и идёт, и скилл не читается вообще:

monorepo/
├── backend/            здесь стартует агент, 40 сессий за 30 дней
│   └── .claude/skills/ читается
└── frontend/
    └── .claude/skills/ лежит на диске, из backend не читается

Никакой ошибки при этом нет. Установка прошла, файл корректный, путь правильный, а модель его просто не видит.

Диалог установки об этом предупреждает, и только когда предупреждение заслужено: какая-то часть проекта набрала вызовы за 30 дней, а выбранная папка нет. Тогда эта часть названа, и переключиться на неё можно одним нажатием. Если истории запусков нет вовсе, диалог молчит, потому что сравнивать не с чем.

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

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

Инструкции: файл, который агент читает раньше скиллов

CLAUDE.md и AGENTS.md это третье место, где живёт контекст проекта, рядом со скиллами и MCP-серверами, и агент читает их раньше любого скилла. Onplate находит их в корне проекта и в его частях и разбирает каждый:

Что видно Зачем это нужно
Упомянутые скиллы и субагенты со состоянием «лежит в проекте», «лежит в backend», «глобальный», «в каталоге, но не установлен», «не найден нигде»
Вызовы каждого упоминания упомянут в инструкциях и ни разу не сработал – повод переписать описание
@-импорты и их размер они тратят токены каждую сессию, и видно их только здесь
Блоки Onplate этот абзац записала установка пакета, а не человек

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

Файл вида CLAUDE.local.md получает отметку «личный». Он обычно в .gitignore, поэтому у коллеги на том же репозитории его нет, и правило, существующее на одной машине, объясняет целый класс историй «у меня работает».

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

Сессии: что сработало на самом деле

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

Claude Code   записанный вызов Skill или Task
Codex         SKILL.md, открытый из известной папки скиллов
OpenCode      записанный вызов skill

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

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

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

Профиль проекта

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

.onplate/
  profile.json                      манифест: имя, версия, контрольная сумма, цели
  README.md                         для тех, кто Onplate не пользуется
  files/skills/go-review/SKILL.md   тела скиллов отдельными файлами
  files/agents/db-migrator.md

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

Сколько у проекта профилей, следует из его формы:

Форма Профилей Где
Одна папка один корень репозитория
Монорепозиторий один в корне, а записи могут называть части
Рабочая папка с клонами по одному на клон корень каждого репозитория

В монорепозитории полезна как раз последняя колонка: скилл про миграции пишется в профиль как принадлежащий backend, а не корню. Это тот же факт про каталог запуска из четвёртого действия, только в форме, которую наследует вся команда.

На практике круг проходит в два шага. Один разработчик настраивает проект локально, ставит скилл для ревью, MCP-сервер для базы и субагента для тестов, добавляет их в профиль и отправляет папку .onplate обычным pull request. Коллега забирает изменения, открывает тот же репозиторий и нажимает «Посмотреть, что установить». До применения виден точный список, а секреты для MCP-сервера вводятся отдельно и локально. В итоге у обоих одинаковая воспроизводимая настройка, а личные глобальные скиллы остались личными.

Четыре правила делают файл безопасным для коммита. Секреты хранятся только именами, никогда значениями. Исполняемого содержимого в профиле нет: только скиллы, серверы и субагенты. Более новая версия никогда не откатывается сама, а расхождение создаёт новую версию вместо перезаписи. И коммит за вас никто не делает, fetch не выполняется: после записи профиля посмотреть диф, закоммитить и запушить остаётся решением человека.

Обратное направление это та же кнопка с противоположным смыслом. «Установить настройки из профиля» применяет то, о чём договорилась команда, и показывает план до прикосновения к файлам. «Добавить локальные настройки в профиль» предлагает команде то, что уже работает локально, и именно так личная настройка обычно становится общей.

Командный реестр и роли

Профиль проекта описывает один репозиторий. Когда набор ресурсов общий для всей команды, его источником может быть отдельный реестр в Git-репозитории, где перечислены роли и нужные этим ролям пакеты.

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

Режимы работы проекта

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

Onplate называет такое сочетание режимом. В каждом проекте есть режимы-заготовки, к ним можно добавить свои. До запуска показаны точная команда, ресурсы, попадающие в контекст, выбранные разрешения и таблица совместимости, которая говорит, какие настройки понимает выбранный клиент. Запуск открывает сессию, не меняя файлов; запись настроек в конфигурацию клиента это отдельное действие с видимым дифом и копией прежнего файла.

Что настроено и чем работали на самом деле показано отдельно, потому что это факты разного происхождения. Сколько сессий открыл сам Onplate, он знает точно, потому что открывал их он. Какими моделями шла работа, вычитано из журналов самих клиентов, и имя модели там такое, какое записал клиент.

Свои режимы выгружаются в .onplate/modes.json и уезжают обычным pull request. Приехавшие из репозитория режимы сами не применяются, а на всё, что расширяет права (работа без подтверждений, доступ за пределы проекта), согласие даётся отдельно.

Сравнение двух проектов

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

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

Видов различий четыре, и каждый назван словами: установлен в разные клиенты, разные версии, с установкой на одной стороне что-то не так, заметно разный вес в контексте. У последнего есть порог в 200 токенов, и интересен именно порог. Два живых файла CLAUDE.md почти никогда не совпадают байт в байт, поэтому отметка разницы в десять токенов завалила бы экран строками, которые ничего не объясняют.

Файлы инструкций сравниваются по пути от корня проекта, потому что абсолютные пути у двух проектов различаются по определению. Глобальные ресурсы из сравнения исключены целиком: они одинаковы для любых двух проектов на этой машине, строка «есть у обоих» была бы шумом, и поэтому счёт токенов покрывает только проектную область и инструкции. Экран говорит об этом сам, вместо того чтобы оставить число без объяснения.

Как разобраться, почему агент ведёт себя иначе

  1. Сравнить два проекта до того, как читать любой из них. Экран показывает только различия, и четыре названных вида покрывают большую часть поломок: область, версия, сломанная установка, вес в контексте.
  2. Проверить, из какой папки стартует агент. Число вызовов показано рядом с каждой частью. Скилл, установленный в часть без сессий, лежит на диске вне досягаемости.
  3. Прочитать файлы инструкций на упоминания, которые указывают в пустоту. Названный несуществующий скилл хуже отсутствия упоминания, потому что агент заполнит пробел сам.
  4. Разделять доступность и использование. Карта связей говорит, до чего можно было дотянуться; вкладка «Сессии» говорит, до чего дотянулись. Ресурс, связанный тремя способами и ни разу не сработавший, это ровно то сочетание, которое стоит искать.
  5. Смотреть на происхождение, а не на счётчик. Группа «приехало вместе с кодом» объясняет большую часть сюрпризов в незнакомом репозитории, и больше её не показывает ничто.

Подводные камни

Корректная установка может быть недосягаемой. Скиллы читаются относительно каталога запуска, поэтому frontend и backend это две разные конфигурации внутри одного репозитория, хотя .git у них общий.

Личный файл инструкций объясняет ненаходимую ошибку. CLAUDE.local.md обычно в .gitignore, поэтому правило, живущее на одной машине, не найдёт никто, кто сравнивает репозиторий.

Профиль не требует глобальных ресурсов, и это замысел. Глобальная строка с отметкой «нет в профиле проекта» это не дефект, который надо исправить, и чтение её как дефекта приводит к коммиту личных предпочтений в общий репозиторий.

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

Папка с репозиториями это не проект. Добавление ~/code даёт один профиль и одну карточку на десяток несвязанных кодовых баз, поэтому Onplate предлагает добавить их по отдельности.

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

Что из этого следует

  • То, что получит агент в папке, это не одна настройка. Это произведение области установки, приоритета клиента, файлов инструкций и каталога, из которого стартовала сессия, и любая из четырёх причин может сделать корректную установку невидимой.
  • Самая полезная группировка идёт по происхождению, а не по виду. «Приехало вместе с кодом» это большая часть содержимого живого репозитория и та группа, которая объясняет неожиданное поведение агента в незнакомом проекте.
  • Доступность и использование это разные вопросы, и им нужны разные экраны. Ресурс может быть связан тремя способами и ни разу не сработать, и отвечает на это только журнал.
  • Профиль стоит коммитить ради ревью, а не ради автоматизации. .onplate/profile.json с телами скиллов отдельными файлами даёт диф, который коллега действительно прочитает в pull request.
  • Каждое число, которое могло бы быть догадкой, либо подтверждено, либо отсутствует. Факт о стеке называет файл и строку, вызов называет своё подтверждение, а неудавшийся расчёт показывает пустоту вместо нуля.

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


Onplate: onplate.tsymbal.dev · документация