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

Хелпер:

  1. Валидирует и pattern, и rewrite на предмет ошибок, которые можно распознать по подсказкам.
  2. Запускает первый проход с --json=compact, чтобы собрать совпадения и показать превью.
  3. Если указан --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. Порядок действий:

  1. Поищите, чтобы подтвердить совпадения: helper search '<pattern>' --lang X .
  2. Прогоните переписывание в dry-run: helper replace '<pattern>' '<rewrite>' --lang X . (без --apply)
  3. Изучите сводку dry-run: число совпадений, затронутые файлы, превью по каждому месту.
  4. Если не то: уточните паттерн, вернитесь к шагу 1.
  5. Если то: helper replace '<pattern>' '<rewrite>' --lang X . --apply.

Никогда не применяйте переписывание, которое не прогнали через dry-run. После --apply в git-репозитории проверьте результат через git diff --stat, прежде чем коммитить.


Когда sg возвращает 0 совпадений, а вы знаете, что код там есть

По приоритету:

  1. Запустите helper validate '<pattern>' --lang <lang> — отловит неправильное использование regex, отсутствующие тела функций, завершающие двоеточия в Python.
  2. Проверьте --lang — sg определяет язык по расширению; если передать .tsx-файл с --lang ts (а не tsx), JSX не распарсится.
  3. Посмотрите на разобранный паттерн: sg run -p '<pattern>' --lang <lang> --debug-query=ast --stdin <<< '<sample>'. Если видите узлы ERROR — паттерн некорректен.
  4. Проверьте AST целевого файла: sg run -p '$_' --lang <lang> --debug-query=cst path/to/file | head -40 — найдите kind, который пытаетесь сматчить.
  5. Попробуйте 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, ... }.
  • Без --json sg выдаёт человекочитаемый цветной вывод, пригодный для терминала.
  • Вывод хелпера по умолчанию человекочитаемый (file:line:column + превью совпадения). Передайте --json-out для сырого JSON.
  • replace хелпера всегда выводит сводку: число совпадений, число файлов, превью по каждому месту.

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


Обязательное чтение (по приоритету)

  1. references/patterns.md — метапеременные, правила именования, уровни строгости. Читайте, когда непонятно, почему паттерн не матчится.
  2. references/pitfalls.md — полевой справочник режимов отказа. Читайте, когда 0 совпадений вас удивляет.
  3. references/recipes.md — готовые паттерны по языкам, копируй и вставляй. Читайте первым при старте новой задачи.
  4. references/cli.md — sg run, sg scan, sg test, sg new, sg lsp. Читайте, когда хелпера не хватает.
  5. references/yaml-rules.md — схема YAML-правил. Читайте, когда переросли инлайновые паттерны.
  6. references/sgconfig.md — конфигурация уровня проекта. Читайте, когда настраиваете sg scan для реального проекта.
  7. 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.
перевод и редактура:

обновлено

⌕ ESC