ast-grep
Use when asked to run AST-based structural search, lint, or rewrite of code when regex is too fragile. Not for remote, credential, publish, deploy, or irreversible changes.
SKILL.md.devin/skills/ast-grep/SKILL.md
GitHubast-grep
Contract
| Field | Bound contract |
|---|---|
| Trigger | AST-based modification, structural search, lint, or replacement too fragile for regex. |
| Authority | Reversible local: writes only VCS-tracked source files (search is read-only; rewrites apply only through the helper after dry-run review); rollback is version control. No remote mutation. |
| Side effect | Local file writes through the helper two-pass validate/dry-run/apply flow; no remote, credential, or published mutation. |
| Done | Pattern validated and blast radius reviewed; any rewrite landed at the correct scope (search/lint-only runs are valid). |
Inputs
- An ast-grep pattern, single-quoted in the shell so
$VARreaches ast-grep unexpanded. - A language (
--lang) or a single target path whose extension auto-detects it; required for stdin patterns. - For rewrites: a rewrite template and one or more target paths (defaults to the current directory).
- Optional: include/exclude globs (repeatable, prefix
!to exclude), context lines, JSON output mode.
Procedure
- Confirm the task is structural (call, function, class, or import shaped like a pattern), not text/regex/filename matching (use grep) or semantic type/reference lookup (use LSP or the compiler). ast-grep matches syntax, not bytes. Done when: the task is confirmed structural.
- Validate the pattern before searching:
python3 scripts/ast_grep_helper.py validate '<pattern>' --lang <L>. Exit 0 means ast-grep parses it cleanly; exit 2 means malformed (the helper prints the parsed pattern tree showing the ERROR node). Fix and re-validate. Done when: validate exits 0. - For a rewrite, run the dry-run:
python3 scripts/ast_grep_helper.py replace '<pattern>' '<rewrite>' --lang <L> <paths>. Read the diff and theN matches across M filescount. If the blast radius is wrong, stop and refine the pattern (tighten meta-variables, add--lang, add context); re-run the dry-run. Done when: the dry-run diff and match count are correct. - Apply only after the dry-run diff is correct:
python3 scripts/ast_grep_helper.py replace '<pattern>' '<rewrite>' --lang <L> <paths> --apply. The helper writes via a separate--update-allpass. Done when: files are updated via--update-all. - Invoke
ast-grep, neversg:sgcollides with thesetgroupsbinary on many systems. Done when:ast-grepis invoked, notsg.
Pattern syntax: $VAR matches any single node; $$$ARGS matches zero or more nodes; $_ matches any node (non-capturing).
Invariants: validate before searching; dry-run before applying; single-quote patterns; --lang is required for stdin; a pattern is code, not regex; switch to grep the moment |, .*, \w, or [...] would be needed. The helper keeps --json and --update-all as separate passes because combining them makes --json silently win and the write is dropped with no error.
For complex tasks, ast-grep supports YAML rule files (sgconfig.yml, ast-grep new project) with rule, fix, inside, any, and matches fields; invoke ast-grep scan/run/test directly for these.
Failure and recovery
- Malformed pattern:
validateexits 2 and prints the pattern debug tree with the ERROR node. No files touched. Fix the pattern and re-validate. - Wrong blast radius: the dry-run diff or match count is not as expected. Do not
--apply. Refine the pattern and re-run the dry-run. No files touched. - Zero matches unexpectedly: run the 0-matches ladder in order: (1) validate the pattern; (2) check
--lang(tsxis notts; the wrong dialect silently matches nothing); (3)ast-grep run -p '<pattern>' -l <L> --debug-query=patternand look forERROR; (4) inspect the target's actual tree with--debug-query=aston a known-matching snippet; (5) reproduce in the online playground. No files touched until a correct match is confirmed. - ast-grep binary absent:
validateskips the parse check (regex-smell only) and warns;replaceexits 2 without running. Install ast-grep before proceeding. - Apply pass failure: the helper reports the ast-grep error and returns non-zero. Revert the partially-written VCS-tracked targets via version control and re-run the full dry-run/apply sequence.
- Partial-result rule: a failed apply pass leaves whatever ast-grep wrote; never report done. Recover via version control and re-run from the dry-run.
Output
validate: exit 0 (valid) or 2 (malformed, with the pattern debug tree); advisory regex-smell hints on stderr.replacedry-run: a compact unified diff and anN matches across M filessummary; no files modified.replace --apply: VCS-tracked files updated via--update-allplus a confirmation line.- Direct
ast-grepsearch: matched code locations.