Ast Grep
Структурный поиск и переписывание кода на уровне AST через ast-grep.
Метаданные навыка
| Источник | Опциональный (устанавливается по запросу) — установите командой hermes skills install official/software-development/ast-grep |
| Путь | optional-skills/software-development/ast-grep |
| Версия | 1.0.0 |
| Автор | Yeongyu Kim (code-yeongyu), adapted by Hermes Agent |
| Лицензия | MIT |
| Платформы | linux, macos, windows |
| Теги | ast, codemod, refactoring, structural-search, code-search, rewrite, tree-sitter |
| Связанные навыки | simplify-code, systematic-debugging |
Справочник: полный SKILL.md
Ниже приведено полное определение навыка, которое Hermes загружает при его активации. Именно это видит агент в качестве инструкций, пока навык активен.
ast-grep
ast-grep (бинарник также называется sg) — инструмент AST-осведомлённого поиска и переписывания кода, работающий с 25 языками. Паттерн он воспринимает как код: разбирает его точно так же, как парсит ваш проект, и ищет совпадения по структуре. Берите этот инструмент всякий раз, когда ответ зависит от формы кода, а не от байтов текста.
Навык поставляется с Python-обёрткой scripts/ast_grep_helper.py и скриптами установки под платформу: install.sh (POSIX) и install.ps1 (Windows). Хелпер добавляет офлайн-валидацию паттернов, трюк с двумя проходами записи и автоматический поиск бинарника. Используйте его как точку входа по умолчанию.
Источник апстрима: заимствовано из code-yeongyu/ast-grep-skill (MIT), как поставляется в бандле shared-skills проекта oh-my-openagent.
Когда использовать этот навык
Применяйте его всякий раз, когда вопрос касается структуры кода, а не байтов:
- «Найти каждую функцию, принимающую параметр
Request.» - «Переписать каждый
console.log(x)вlogger.info(x).» - «Убрать все приведения
as any.» - «Заменить
require(...)наimportпо всему репозиторию.» - «Найти пустые блоки catch.»
- «Мигрировать
Optional[X]вX | None.» - «Применить этот кодмод к этим 200 файлам.»
- «Прогнать наши YAML-правила линтера и показать нарушения.»
Переключайтесь на search_files (или обычный rg), когда вопрос текстовый по своей природе (содержимое строковых литералов, комментарии, заголовки лицензий, имена файлов, кросс-языковой regex). Если сомневаетесь, спросите себя: «ответ зависит от синтаксического дерева языка или просто от байтов файла?» В первом случае — ast-grep, во втором — search_files.
Заметки по интеграции с Hermes:
- Запускайте хелпер и
sgчерез инструментterminal. Берите каждый паттерн в одинарные кавычки, чтобы шелл не разворачивал$VAR. - Для цепочек «найти → прочитать» вокруг совпадений используйте
--json-outи обрабатывайте результат черезexecute_code, а не прогоняйте через интерпретаторы по пайпу. - Это дополняет (а не заменяет) инструмент Hermes
patch:patch— для точечных правок, которые вы пишете сами; ast-grep — для массовых переписываний по паттерну сразу во множестве мест.
Три вещи, которые агент должен усвоить
1. ast-grep — это НЕ regex
Подстановочные знаки — это $VAR (один узел AST) и $$$ (ноль или больше узлов). Синтаксис regex молча ломается:
| Вы написали | Что увидел ast-grep | Что вы хотели |
|---|---|---|
foo|bar | побитовое ИЛИ foo и bar | запустить два отдельных поиска |
.*foo | не парсится | $$$ foo (если $$$ — список узлов) или используйте rg |
\w+ | не парсится | $VAR, чтобы захватить любой идентификатор |
[a-z] | символьный класс, не парсится | переключитесь на rg |
Полная таблица антипаттернов — в references/pitfalls.md §1. Подкоманда validate хелпера отлавливает такие случаи механически — вызывайте её прежде, чем разбирать «0 совпадений» вручную.
2. Паттерны должны быть валидным кодом
Сам паттерн должен парситься. def $FN($$$): падает, потому что завершающее : делает конструкцию неполной — используйте def $FN($$$). function $NAME без параметров и тела тоже падает — используйте function $NAME($$$) { $$$ }. Полная таблица по языкам — в references/pitfalls.md §2.
3. --update-all и --json взаимно исключают друг друга (молча)
Это самая крупная засада при скриптинге. sg run -p P -r R --json --update-all вернёт JSON, но не изменит файлы. Чтобы и посмотреть превью, и применить изменения, нужны два прохода:
sg run -p P -r R --json=compact . # проход 1: смотрим, что изменится
sg run -p P -r R --update-all . # проход 2: применяем изменения
Хелпер делает это автоматически при вызове replace --apply. Читайте references/pitfalls.md §9.
Скрипт-хелпер — scripts/ast_grep_helper.py
Однофайловая обёртка на Python 3 stdlib. Одинаковая на любой ОС. Точка входа агента по умолчанию.
search — найти все совпадения паттерна
python scripts/ast_grep_helper.py search 'console.log($MSG)' --lang ts src/
Сначала валидирует паттерн офлайн. Если паттерн похож на regex (\w, .*, | и т.п.), хелпер завершится с подсказкой и вообще не вызовет sg — экономит лишний проход. Передайте --force, чтобы пропустить валидацию.
Флаги:
--lang ts(или любой из 25 языков; принимаются алиасы вродеjs,py,rs,kt)--globs '!**/*.test.ts'(можно указывать многократно; префикс!— для исключения)-C 3(строки контекста)--json-out(сырой JSON вместо человекочитаемого формата)
replace — переписать по паттерну, по умолчанию dry-run
# Предпросмотр dry-run (по умолчанию — файлы не меняются)
python scripts/ast_grep_helper.py replace 'console.log($MSG)' 'logger.info($MSG)' --lang ts src/
# Применить по-настоящему
python scripts/ast_grep_helper.py replace 'console.log($MSG)' 'logger.info($MSG)' --lang ts src/ --apply
Хелпер:
- Валидирует и
pattern, иrewriteна предмет ошибок, которые можно распознать по подсказкам. - Запускает первый проход с
--json=compact, чтобы собрать совпадения и показать превью. - Если указан
--apply, запускает второй проход с--update-all, чтобы изменить файлы.
scan — прогнать YAML-правила
# Найти sgconfig.yml от текущей директории и прогнать все правила
python scripts/ast_grep_helper.py scan src/
# Прогнать один файл правила
python scripts/ast_grep_helper.py scan -r rules/no-console.yml src/
# Применить автофиксы
python scripts/ast_grep_helper.py scan -U src/
# GitHub-аннотации, удобные для CI
python scripts/ast_grep_helper.py scan --report-style short src/
validate — офлайн-проверка паттерна (без вызова sg)
Пригодится для CI-линтов, pre-commit хуков и быстрых проверок на здравый смысл:
python scripts/ast_grep_helper.py validate '\w+' --lang ts
# → exit 2: regex \w не поддерживается. Используйте $VAR для идентификаторов.
python scripts/ast_grep_helper.py validate 'console.log($MSG)' --lang ts
# → exit 0: паттерн выглядит правдоподобно для ast-grep.
langs / doctor / install
python scripts/ast_grep_helper.py langs # вывести список 25 поддерживаемых языков и алиасов
python scripts/ast_grep_helper.py doctor # проверить доступность бинарника ast-grep
python scripts/ast_grep_helper.py install # делегировать в install.sh / install.ps1
Подкоманды new и test напрямую проксируют в sg new и sg test.
Прямой вызов sg (когда хелпера не хватает)
У хелпера свой взгляд на вещи. Для полного контроля спускайтесь на уровень sg. Навык поставляется со шпаргалкой по CLI в references/cli.md. Минимальный набор идиом:
# Поиск
sg run -p 'console.log($MSG)' --lang ts src/
# Поиск с JSON-выводом для скриптинга
sg run -p 'console.log($MSG)' --lang ts --json=compact src/
# Переписывание, dry-run
sg run -p 'console.log($MSG)' -r 'logger.info($MSG)' --lang ts --json=compact src/
# Переписывание, применение
sg run -p 'console.log($MSG)' -r 'logger.info($MSG)' --lang ts --update-all src/
# Паттерн из stdin (отлично подходит для быстрых экспериментов)
echo 'console.log("hi")' | sg run -p 'console.log($MSG)' --lang js --stdin
# Отладка паттерна, который не даёт совпадений
sg run -p '<your pattern>' --lang <lang> --debug-query=ast --stdin <<< '<sample-code>'
# Прогон YAML-правил
sg scan src/
# Инлайновое YAML-правило (разовое)
sg scan --inline-rules '
id: no-todo
language: TypeScript
severity: warning
rule: { pattern: TODO }' src/
При прямом вызове sg в шелле всегда берите паттерны в одинарные кавычки, чтобы шелл не разворачивал $VAR.
Дерево решений — что использовать и когда
USER asks for "find/rewrite/codemod"
│
├─ structural pattern (function shape, call, class, import, control flow)
│ └→ ast-grep (this skill)
│
├─ text pattern (regex, alternation, character classes, file names)
│ └→ search_files / rg
│
├─ semantic question (what variable does this refer to? does this throw?)
│ └→ LSP tools, TypeScript compiler, Pyright, Semgrep with type inference
│
└─ multiple repos / federated search
└→ a search engine + then ast-grep / rg / LSP per-repo
Если пользователь говорит «найди всё» или «каждый», по умолчанию используйте ast-grep, когда цель имеет форму (функция, класс, вызов, импорт, оператор). По умолчанию используйте search_files, когда цель текстовая (содержимое строки, комментарий, заголовок лицензии, имя файла, часть идентификатора).
Всегда сначала прогоняйте dry-run при переписывании
Плохой паттерн молча переписывает не то. Именно поэтому replace хелпера по умолчанию работает в режиме dry-run. Порядок действий:
- Поищите, чтобы подтвердить совпадения:
helper search '<pattern>' --lang X . - Прогоните переписывание в dry-run:
helper replace '<pattern>' '<rewrite>' --lang X .(без--apply) - Изучите сводку dry-run: число совпадений, затронутые файлы, превью по каждому месту.
- Если не то: уточните паттерн, вернитесь к шагу 1.
- Если то:
helper replace '<pattern>' '<rewrite>' --lang X . --apply.
Никогда не применяйте переписывание, которое не прогнали через dry-run. После --apply в git-репозитории проверьте результат через git diff --stat, прежде чем коммитить.
Когда sg возвращает 0 совпадений, а вы знаете, что код там есть
По приоритету:
- Запустите
helper validate '<pattern>' --lang <lang>— отловит неправильное использование regex, отсутствующие тела функций, завершающие двоеточия в Python. - Проверьте
--lang—sgопределяет язык по расширению; если передать.tsx-файл с--lang ts(а неtsx), JSX не распарсится. - Посмотрите на разобранный паттерн:
sg run -p '<pattern>' --lang <lang> --debug-query=ast --stdin <<< '<sample>'. Если видите узлыERROR— паттерн некорректен. - Проверьте AST целевого файла:
sg run -p '$_' --lang <lang> --debug-query=cst path/to/file | head -40— найдитеkind, который пытаетесь сматчить. - Попробуйте playground: <https://ast-grep.github.io/playground.html> — вставьте код и паттерн, посмотрите, что происходит.
Не перебирайте вариации вслепую. У каждого сбоя есть причина — вскрывайте её.
Когда использовать YAML-правила, а когда инлайновые паттерны -p
Используйте инлайновый -p, когда:
- разовый запрос по случаю;
- паттерн простой (без ограничений, без шаблона фикса);
- вы находитесь в режиме исследования.
Используйте YAML-правила (файл в rules/, запуск через sg scan), когда:
- паттерн переиспользуется (правило линтера, кодмод, который гоняется в CI);
- нужны
constraints,transform, сложныеinside/hasили составная логика; - нужен автофикс (поле
fix:); - нужно протестировать правило (снапшот-тесты через
sg test).
Полная схема YAML-правил — в references/yaml-rules.md. Настройка проекта (sgconfig.yml, ruleDirs, utilDirs) — в references/sgconfig.md.
Дисциплина вывода
sg run --json=compactвыдаёт массив объектов-совпадений:{ file, range: {start, end}, text, replacement?, lines, language, ... }.- Без
--jsonsgвыдаёт человекочитаемый цветной вывод, пригодный для терминала. - Вывод хелпера по умолчанию человекочитаемый (file:line:column + превью совпадения). Передайте
--json-outдля сырого JSON. replaceхелпера всегда выводит сводку: число совпадений, число файлов, превью по каждому месту.
При сводке для пользователя всегда указывайте число затронутых файлов, а не только число совпадений. Пользователей волнует масштаб воздействия.
Обязательное чтение (по приоритету)
references/patterns.md— метапеременные, правила именования, уровни строгости. Читайте, когда непонятно, почему паттерн не матчится.references/pitfalls.md— полевой справочник режимов отказа. Читайте, когда 0 совпадений вас удивляет.references/recipes.md— готовые паттерны по языкам, копируй и вставляй. Читайте первым при старте новой задачи.references/cli.md—sg run,sg scan,sg test,sg new,sg lsp. Читайте, когда хелпера не хватает.references/yaml-rules.md— схема YAML-правил. Читайте, когда переросли инлайновые паттерны.references/sgconfig.md— конфигурация уровня проекта. Читайте, когда настраиваетеsg scanдля реального проекта.references/install.md— способы установки по ОС. Читайте, только еслиinstall.sh/install.ps1не сработали.
Инварианты (не нарушать)
- Валидируйте перед поиском. Когда паттерн генерируется программно, сначала вызывайте
helper validate. Он отлавливает класс ошибок с неверным использованием regex, на которые приходится ~70% сессий отладки «0 совпадений». - Dry-run перед применением. Никогда не запускайте
sg run -r ... --update-all, не проверив сначала совпадения.replaceхелпера соблюдает это правило по умолчанию. - Запись в два прохода. При прямом использовании
sgдля превью и применения запускайте два вызова —--jsonигнорирует--update-all. - Одинарные кавычки для паттернов в шелле.
'$VAR', а не"$VAR". В двойных кавычках шелл разворачивает$VARв пустую строку и ломает паттерн. - Паттерн — это код, а не regex. Если паттерну нужны
|,.*,\wили[a-z], переключайтесь на search_files. Не пытайтесь силой втиснуть ast-grep в форму regex. --langобязателен для stdin. При работе через пайп с--stdinуказывайте--langявно —sgне может определить его по расширению.- Linux: предпочитайте
ast-grep, а неsg, потому чтоsgконфликтует сsetgroupsизutil-linux. Хелпер уже это учитывает; если вызываетеsgнапрямую — задайте алиас:alias sg=ast-grep.