name: ast-grep description: >- Write, debug, and validate ast-grep structural code search rules. Use this skill when the user needs syntax-aware code search, AST pattern matching, structural refactor discovery, language-construct queries, or searches that plain text tools like rg can miss, such as finding functions with particular descendants, calls inside specific contexts, missing error handling, React hook shapes, decorators, or other Tree-sitter-backed code structures. version: 0.1.0 category: developer-tools-integrations tags: - ast-grep - structural-search - code-search - tree-sitter - static-analysis - refactoring argument-hint: "[code-pattern-or-search-goal] [path]" allowed-tools: Read, Glob, Grep, Bash, Write
ast-grep Structural Search
Use ast-grep when the user needs code structure, not just text. The useful outcome is a validated rule plus a clear statement of what it matches and what it may miss.
Triage
Start with the smallest tool that can answer the question.
- Use
rgfor exact names, strings, comments, or simple text. - Use language tooling for semantic facts such as type resolution, references, imports, or rename safety.
- Use ast-grep for syntax shape: descendants, ancestors, call forms, decorators, missing constructs, nested contexts, or multi-language structural searches.
If ast-grep is not installed, say so and offer an rg fallback only when the
fallback will not pretend to be structural.
Workflow
Identify the language and target files.
- Infer from paths and extensions when possible.
- Ask only if the language or include/exclude scope cannot be inferred.
Write a tiny fixture before searching the real repository.
- Include at least one positive example.
- Add a negative example when false positives would be costly.
Start with the simplest pattern.
- Use
ast-grep run --pattern ... --lang ...for a single-node search. - Move to a YAML rule when the search needs
has,inside,not,any,all,regex,nthChild, or reusable metadata.
- Use
Debug the parsed structure when the first rule misses.
- Use
ast-grep run --pattern '<sample code>' --lang <lang> --debug-query=ast. --debug-queryprints diagnostic output and may exit non-zero; treat the printed tree as useful debug output, not as a failed repository search.
- Use
Validate on the fixture before running on the repository.
- Keep the rule as simple as possible until it matches the fixture.
- Then scan the real path and report expected false-negative boundaries.
Report the final rule, command, and caveats.
- Name node kinds or language constructs intentionally excluded.
- Prefer JSON output when a downstream script will consume matches.
Rule File First
Rule files avoid most shell quoting problems and are the preferred form for complex searches.
On Windows, save rule files as UTF-8 without BOM. If ast-grep scan --rule
reports missing field language even though the YAML has language, check for
a BOM at the start of the file.
# async-await.yml
id: async-await
language: javascript
rule:
any:
- kind: function_declaration
- kind: arrow_function
- kind: method_definition
has:
pattern: await $EXPR
stopBy: end
$rule = @'
id: async-await
language: javascript
rule:
any:
- kind: function_declaration
- kind: arrow_function
- kind: method_definition
has:
pattern: await $EXPR
stopBy: end
'@
[System.IO.File]::WriteAllText(
"async-await.yml",
$rule,
[System.Text.UTF8Encoding]::new($false)
)
ast-grep scan --rule async-await.yml .\src
ast-grep scan --rule async-await.yml .\src --json
ast-grep scan --rule async-await.yml ./src
ast-grep scan --rule async-await.yml ./src --json
Inline Smoke Tests
Use inline rules only for quick checks. In PowerShell, single-quoted here-strings
preserve $META variables without escaping.
$rule = @'
id: console-call
language: javascript
rule:
pattern: console.log($$$ARGS)
'@
"console.log('debug', value);" | ast-grep scan --inline-rules $rule --stdin
In POSIX shells, quote the YAML with single quotes or escape $.
printf '%s\n' "console.log('debug', value);" |
ast-grep scan --inline-rules 'id: console-call
language: javascript
rule:
pattern: console.log($$$ARGS)' --stdin
Every scan --inline-rules example must include id, language, and rule.
Debugging Checklist
When a rule produces no matches:
- Confirm the language:
--lang javascriptis different from--lang tsx. - Print the query tree with
--debug-query=astor--debug-query=cst. - Check that rule files are UTF-8 without BOM.
- Replace the complex rule with one
patternor onekind. - Add relational rules back one at a time.
- Add
stopBy: endto deephas/insidesearches. - Check whether metavariables occupy a whole syntax node.
- Broaden node kinds if the language has several forms of the same concept.
Avoid Silent False Negatives
Do not assume one node kind covers a user concept.
For JavaScript and TypeScript, "function" may include:
function_declarationfunctionarrow_functionmethod_definition- class fields or object properties containing functions
For React, decide whether the user means .js, .jsx, .ts, or .tsx.
For Python, inspect the parsed shape before relying on indentation-sensitive multi-line patterns.
Output Contract
When answering an ast-grep task, include:
Rule: the YAML rule or inline pattern.Validation: the fixture command you used or recommend.Repository command: the command for the user's target path.Caveats: likely false positives, false negatives, and language forms not covered.
Reference
Load references/rule_reference.md when the task needs detailed syntax for
atomic rules, relational rules, composite rules, metavariables, or troubleshooting.