Перейти к содержимому

Искать код через граф кода

Это руководство показывает, как поместить репозиторий в граф кода, а затем с его помощью ответить на вопрос, который и встаёт перед рефакторингом: что от этого зависит?

Граф кода — это база символов и рёбер, которая строится разбором ваших зарегистрированных checkout’ов с помощью tree-sitter. Его инструменты предназначены для агента — добраться до них можно, попросив агента.

Граф индексирует Dart, JavaScript, TypeScript, TSX, PHP, Python, Rust, Zig, C, C++, Go, Java, Ruby, C#, Swift, Kotlin, R, Assembly, MATLAB и Ada. Файлы на любом другом языке не разбираются, поэтому они не появляются в search_code, code_symbol, code_callers, code_callees или code_impact.

На то, что попадает в индекс, влияют ещё две вещи:

  • В рабочем дереве git перечисление опирается на git ls-files --cached --others --exclude-standard, поэтому учитываются все .gitignore, вложенные файлы игнорирования, .git/info/exclude и глобальный файл исключений. Именно это удерживает node_modules и артефакты сборки за пределами графа.
  • Сгенерированный Dart отбрасывается в любом случае. Сгенерированные файлы обычно закоммичены, поэтому один git их не исключил бы, и они меняются при каждом запуске кодогенерации.

Регистрация checkout’а запускает RepoAdded, который автоматически стартует для него конвейер index_code. См. Добавить репозитории в рабочее пространство.

Чтобы переиндексировать вручную:

  1. Откройте Настройки → Рабочее пространство → Репозитории.
  2. Нажмите кнопку Индексировать код в строке репозитория.
  3. Пока индексация идёт, кнопка показывает число обработанных файлов из общего, с элементом отмены. Когда она завершается, кнопка превращается в отметку с числом символов.

Граф разбит по рабочему пространству, репозиторию и checkout’у: связанный checkout — одна часть, а каждый worktree пространства получает собственную (с ключом по id его изолированного репозитория).

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

  • Наблюдатель за файлами допускает одновременно только один запуск индексации на хосте — второй checkout ждёт. Конвейер index_code отдельно ограничен одним запуском на рабочее пространство, поэтому два репозитория в одном рабочем пространстве встают в очередь; разные рабочие пространства могут индексироваться параллельно. Сам индексатор сериализует два запуска одного и того же checkout’а, чтобы старт конвейера и обход наблюдателя не разобрали его дважды.
  • Worktree, принадлежащие неактивным пространствам (ни одного сообщения за последнюю неделю), не отслеживаются и не индексируются. Связанные checkout’ы продолжают отслеживаться. Решение о пропуске перепроверяется через пять минут, поэтому пространство, снова ставшее активным, подхватывается.

Попросите агента выполнить поиск. Инструмент называется search_code, и ему нужны и рабочее пространство, и репозиторий:

search_code(workspace_id: "...", repo_id: "...", query: "authentication middleware")

id репозиториев берутся из list_repos. Необязательный аргумент mode принимает значения keyword, semantic или hybrid и по умолчанию равен hybrid — BM25 по именам, сигнатурам и комментариям документации, объединённый с векторным сходством.

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

Когда вы знаете точное имя, вместо этого посмотрите его напрямую:

code_symbol(workspace_id: "...", repo_id: "...", name: "MyClass")

Оба инструмента принимают symbol_id из результата поиска и необязательный limit (по умолчанию 50):

code_callers(workspace_id: "...", symbol_id: "...")
code_callees(workspace_id: "...", symbol_id: "...")

code_callers возвращает символы, у которых есть входящее ребро к этому; code_callees возвращает те, от которых он зависит.

Ради этого инструмента и существует вся страница:

code_impact(workspace_id: "...", symbol_id: "...", depth: 3)

Он вычисляет радиус обратных зависимостей: всё, что прямо или транзитивно зависит от символа по рёбрам calls, extends и implements (импорты, включения mixin’ов и обобщённые ссылки хранятся в графе, но здесь по ним не проходят). depth — максимальное число переходов, от 1 до 6, по умолчанию 2.

Результат — три поля:

  • root — символ, о котором вы спросили
  • impacted — достижимое множество, включая корень на глубине 0, каждый элемент несёт свой depth от корня
  • edgeCount — сколько рёбер пересёк обход, именно число, а не сами рёбра

Запустите его перед переименованием или изменением сигнатуры и передайте агенту список затронутых как его область работы.

Результаты сверяются с рабочей копией самого вызывающего

Заголовок раздела «Результаты сверяются с рабочей копией самого вызывающего»

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

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

Попросите агента выполнить code_impact (или review_studio.blastRadius), когда вам нужен радиус влияния изменения. См. Вычисления проверки.

Семантическому поиску нужна модель эмбеддингов

Заголовок раздела «Семантическому поиску нужна модель эмбеддингов»

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

Установите модель в разделе Настройки → Сервер → Диагностика и конфиденциальность → Семантический поиск до индексации. Символы получают вектор, когда их файл попадает в индекс.

Учтите также, что любая пересборка бинарника сервера заново раскладывает dylib’ы грамматик и меняет их mtime, что обесценивает каждую контрольную точку индекса и вынуждает при следующем запуске заново обойти все checkout’ы целиком. Он пересчитывает хэши, но не извлекает заново неизменившиеся файлы.

Виды символов: function, method, class, field, enum, constructor, getter, setter, typedef, extension, mixin, variable.

Виды рёбер: calls, imports, extends, implements, mixes in, references.

Канонические сигнатуры этих инструментов приведены в разделе Инструменты MCP.