Индекс и файлы исключений отвечают на два разных вопроса, и путать их дорого. Индекс определяет, что агент может быстро найти. Файл .cursorignore определяет, что ему вообще недоступно: перечисленные пути закрываются для агента, подсказок в редакторе, точечной правки и упоминаний. Рядом есть отдельный файл, который исключает только из индекса, - содержимое при этом остаётся доступным для прямого чтения. Разница в одну строку конфигурации и в целый класс ожиданий: в первом случае файла для редактора нет, во втором он просто не находится поиском.
Наивная модель - записать секреты в исключения и считать вопрос закрытым. Она разбивается о простой факт: исключение действует на функции самого редактора, но не останавливает терминал и не ограничивает инструменты подключённых MCP-серверов. Команда, запущенная агентом, читает файловую систему так же, как любой процесс с вашими правами. Документация прямо предупреждает, что игнорирование не даёт полной защиты, - и это стоит принять всерьёз, а не как формальную оговорку.
Отсюда правильная роль исключений: это гигиена контекста, а не граница безопасности. Они убирают из поля зрения то, что не должно попадать в запросы и индекс, - секреты, дампы, сгенерированный код, вендорные каталоги. Настоящая защита строится другими средствами: правами файловой системы, песочницей, подтверждениями, hooks и отдельными учётными данными для агента. Одно дополняет другое, но не заменяет, и различать их важно ровно потому, что первое выглядит как второе.
Полезно один раз увидеть осмысленный список исключений. Ниже - переменные окружения во всех формах, файлы учётных данных, приватные ключи, каталог сборки и сгенерированные вендорные файлы. Синтаксис привычен по .gitignore, и это удобно, но у него есть тонкость: отрицание не вернёт глубоко вложенный файл, если родительский каталог исключён целиком - путь приходится раскрывать по уровням.
Сам индекс устроен аккуратнее, чем можно подумать. Пути шифруются, поиск работает по эмбеддингам, исходный код не хранится в открытом виде, а куски файлов держатся в памяти только на время индексирования. Для собственного шифрования путей есть отдельный файл ключей. Практический вывод из этого не в том, чтобы расслабиться, а в том, чтобы понимать, какие именно данные покидают машину и в каком виде: у вопроса есть документированный ответ, и на него можно ссылаться в разговоре с безопасностью.
Многокорневые рабочие пространства - отдельный случай. Каждый корень индексируется сам по себе, а функции, которым нужен один общий git-корень, в такой конфигурации могут быть недоступны. Это не поломка, а следствие устройства: если вы держите три репозитория в одном окне, часть механизмов просто не знает, какой из них считать главным. Планировать это лучше заранее, чем выяснять на середине задачи.
Практический сценарий, из-за которого стоит различать защиту и гигиену, выглядит так. Агент запускает команду, команда падает, и в выводе оказывается строка подключения с паролем. Файл исключений тут ни при чём: он закрывает содержимое от функций редактора, а вывод пришёл из процесса и попал в разговор целиком. Отсюда практика: отдельные учётные данные для агентской работы, переменные окружения, видимые только нужному процессу, и привычка прикладывать фрагмент вывода, а не весь буфер.
Вторая причина писать исключения аккуратно - качество поиска. Сгенерированные каталоги, собранные бандлы и вендорные копии повторяют исходный код почти дословно, поэтому поиск честно находит их наравне с оригиналом. Признак известен всем, кто это проходил: агент уверенно правит файл в каталоге сборки, проверка проходит, а на следующей сборке правка исчезает. Исключить такие каталоги из индекса дешевле, чем объяснять это в инструкции каждый раз.
Инженерный вывод простой. Исключения пишут не по остаточному принципу, а вместе с первой настройкой проекта: файл с секретами, каталоги сборки и вендора, всё, что генерируется. Это разом снижает и стоимость контекста, и вероятность утечки в транскрипт. Но там, где цена ошибки реальна, рядом обязательно стоят права, песочница и отдельные учётные данные.
Типичные провалы предсказуемы. Считать файл исключений защитой от терминала и MCP. Написать отрицание внутри исключённого каталога и удивиться, что файл всё равно не виден. Забыть про многокорневое пространство и ждать работы функций, требующих одного git-корня. И оставить в индексе сгенерированные каталоги, оплачивая их в каждом поиске.
# .cursorignore - синтаксис .gitignore
**/.env
**/.env.*
**/credentials.json
**/*.pem
dist/
vendor/generated/**
# отрицание не вернёт файл, если родительский каталог исключён целиком