1---
2name: ast-grep
3description: Guide for writing ast-grep rules to perform structural code search and analysis. Use when users need to search codebases using Abstract Syntax Tree (AST) patterns, find specific code structures, or perform complex code queries that go beyond simple text search. This skill should be used when users ask to search for code patterns, find specific language constructs, or locate code with particular structural characteristics.
4metadata:
5 source: https://github.com/ampcode/amp-contrib
6---
7
8This skill helps translate natural language queries into ast-grep rules for structural code search. ast-grep uses Abstract Syntax Tree (AST) patterns to match code based on its structure rather than just text, enabling powerful and precise code search across large codebases.
9
10## When to Use This Skill
11
12Use this skill when users:
13
14- Need to search for code patterns using structural matching (e.g., "find all async functions that don't have error handling")
15- Want to locate specific language constructs (e.g., "find all function calls with specific parameters")
16- Request searches that require understanding code structure rather than just text
17- Ask to search for code with particular AST characteristics
18- Need to perform complex code queries that traditional text search cannot handle
19
20## General Workflow
21
22Follow this process to help users write effective ast-grep rules:
23
24### Step 1: Understand the Query
25
26Clearly understand what the user wants to find. Ask clarifying questions if needed:
27
28- What specific code pattern or structure are they looking for?
29- Which programming language?
30- Are there specific edge cases or variations to consider?
31- What should be included or excluded from matches?
32
33### Step 2: Create Example Code
34
35Write a simple code snippet that represents what the user wants to match. Save this to a temporary file for testing.
36
37**Example:**
38If searching for "async functions that use await", create a test file:
39
40```javascript
41// test_example.js
42async function example() {
43 const result = await fetchData();
44 return result;
45}
46```
47
48### Step 3: Write the ast-grep Rule
49
50Translate the pattern into an ast-grep rule. Start simple and add complexity as needed.
51
52**Key principles:**
53
54- Always use `stopBy: end` for relational rules (`inside`, `has`) to ensure search goes to the end of the direction
55- Use `pattern` for simple structures
56- Use `kind` with `has`/`inside` for complex structures
57- Break complex queries into smaller sub-rules using `all`, `any`, or `not`
58
59**Example rule file (test_rule.yml):**
60
61```yaml
62id: async-with-await
63language: javascript
64rule:
65 kind: function_declaration
66 has:
67 pattern: await $EXPR
68 stopBy: end
69```
70
71See `references/rule_reference.md` for comprehensive rule documentation.
72
73### Step 4: Test the Rule
74
75Use ast-grep CLI to verify the rule matches the example code. There are two main approaches:
76
77**Option A: Test with inline rules (for quick iterations)**
78
79```bash
80echo "async function test() { await fetch(); }" | ast-grep scan --inline-rules "id: test
81language: javascript
82rule:
83 kind: function_declaration
84 has:
85 pattern: await \$EXPR
86 stopBy: end" --stdin
87```
88
89**Option B: Test with rule files (recommended for complex rules)**
90
91```bash
92ast-grep scan --rule test_rule.yml test_example.js
93```
94
95**Debugging if no matches:**
96
971. Simplify the rule (remove sub-rules)
982. Add `stopBy: end` to relational rules if not present
993. Use `--debug-query` to understand the AST structure (see below)
1004. Check if `kind` values are correct for the language
101
102### Step 5: Search the Codebase
103
104Once the rule matches the example code correctly, search the actual codebase:
105
106**For simple pattern searches:**
107
108```bash
109ast-grep run --pattern 'console.log($ARG)' --lang javascript /path/to/project
110```
111
112**For complex rule-based searches:**
113
114```bash
115ast-grep scan --rule my_rule.yml /path/to/project
116```
117
118**For inline rules (without creating files):**
119
120```bash
121ast-grep scan --inline-rules "id: my-rule
122language: javascript
123rule:
124 pattern: \$PATTERN" /path/to/project
125```
126
127## Rewriting Code with ast-grep
128
129ast-grep is a powerful AST-based tool that can search for code patterns and transform them into new code. It works like a syntax-aware sed/grep that understands code structure rather than just text.
130
131### Method 1: Command Line with `--rewrite`
132
133The simplest approach is using the `--rewrite` (or `-r`) flag directly in your terminal:
134
135```bash
136ast-grep run --pattern 'foo' --rewrite 'bar' --lang python
137```
138
139This finds all occurrences of `foo` and replaces them with `bar`. A practical example:
140
141```bash
142# Convert old-style property checks to optional chaining
143ast-grep -p '$PROP && $PROP()' --rewrite '$PROP?.()' --interactive -l ts ./src
144```
145
146**Key flags:**
147
148- `--interactive` or `-i`: Review each change before applying
149- `--update-all` or `-U`: Apply all changes without confirmation
150
151### Method 2: YAML Rules with `fix`
152
153For more complex transformations, use YAML rule files with the `fix` field:
154
155```yaml
156id: change_def
157language: Python
158rule:
159 pattern: |
160 def foo($X):
161 $$$S
162fix: |-
163 def baz($X):
164 $$$S
165```
166
167Run with: `ast-grep scan -r rule.yml ./src`
168
169### Meta-Variables
170
171Meta-variables are the key to powerful rewrites. They act like capture groups in regex:
172
173| Meta-variable | Matches |
174| ------------- | -------------------------------------------------- |
175| `$NAME` | Any single AST node (expression, identifier, etc.) |
176| `$$$ITEMS` | Multiple nodes (like function arguments) |
177
178**Example** — Swapping assignment sides:
179
180```yaml
181rule:
182 pattern: $X = $Y
183fix: $Y = $X
184```
185
186Transforms `a = b` into `b = a`.
187
188### Indentation Sensitivity
189
190ast-grep preserves indentation in rewrites. If your fix template has indentation, it's maintained relative to the original code position:
191
192```yaml
193rule:
194 pattern: "$B = lambda: $R"
195fix: |-
196 def $B():
197 return $R
198```
199
200### Expanding the Match Range with FixConfig
201
202Sometimes you need to delete surrounding characters (like commas). Use `FixConfig` with `expandStart` and `expandEnd`:
203
204```yaml
205rule:
206 kind: pair
207 has:
208 field: key
209 regex: Remove
210fix:
211 template: ""
212 expandEnd: { regex: "," } # Also deletes trailing comma
213```
214
215This removes the matched node _plus_ any trailing comma.
216
217### Advanced Features: Rewriters
218
219For complex multi-node transformations, use `rewriters` to process lists of matched nodes:
220
221```yaml
222id: barrel-to-single
223language: JavaScript
224rule:
225 pattern: import {$$$IDENTS} from './module'
226rewriters:
227 - id: rewrite-identifier
228 rule:
229 pattern: $IDENT
230 kind: identifier
231 transform:
232 LIB: { convert: { source: $IDENT, toCase: lowerCase } }
233 fix: import $IDENT from './module/$LIB'
234transform:
235 IMPORTS:
236 rewrite:
237 rewriters: [rewrite-identifier]
238 source: $$$IDENTS
239 joinBy: "\n"
240fix: $IMPORTS
241```
242
243This converts barrel imports like `import { A, B } from './module'` into individual imports.
244
245### Workflow Summary
246
2471. **Find**: Use patterns to match AST nodes
2482. **Capture**: Meta-variables (`$VAR`, `$$$ARGS`) capture matched content
2493. **Transform**: Optionally process captured content (case conversion, regex replacement)
2504. **Patch**: Replace matched nodes with the `fix` template
251
252### Tips
253
254- Use single quotes on command line to prevent shell expansion of `$`
255- Non-matched meta-variables become empty strings in the fix
256
257## ast-grep CLI Commands
258
259### Inspect Code Structure (--debug-query)
260
261Dump the AST structure to understand how code is parsed:
262
263```bash
264ast-grep run --pattern 'async function example() { await fetch(); }' \
265 --lang javascript \
266 --debug-query=cst
267```
268
269**Available formats:**
270
271- `cst`: Concrete Syntax Tree (shows all nodes including punctuation)
272- `ast`: Abstract Syntax Tree (shows only named nodes)
273- `pattern`: Shows how ast-grep interprets your pattern
274
275**Use this to:**
276
277- Find the correct `kind` values for nodes
278- Understand the structure of code you want to match
279- Debug why patterns aren't matching
280
281**Example:**
282
283```bash
284# See the structure of your target code
285ast-grep run --pattern 'class User { constructor() {} }' \
286 --lang javascript \
287 --debug-query=cst
288
289# See how ast-grep interprets your pattern
290ast-grep run --pattern 'class $NAME { $$$BODY }' \
291 --lang javascript \
292 --debug-query=pattern
293```
294
295### Test Rules (scan with --stdin)
296
297Test a rule against code snippet without creating files:
298
299```bash
300echo "const x = await fetch();" | ast-grep scan --inline-rules "id: test
301language: javascript
302rule:
303 pattern: await \$EXPR" --stdin
304```
305
306**Add --json for structured output:**
307
308```bash
309echo "const x = await fetch();" | ast-grep scan --inline-rules "..." --stdin --json
310```
311
312### Search with Patterns (run)
313
314Simple pattern-based search for single AST node matches:
315
316```bash
317# Basic pattern search
318ast-grep run --pattern 'console.log($ARG)' --lang javascript .
319
320# Search specific files
321ast-grep run --pattern 'class $NAME' --lang python /path/to/project
322
323# JSON output for programmatic use
324ast-grep run --pattern 'function $NAME($$$)' --lang javascript --json .
325```
326
327**When to use:**
328
329- Simple, single-node matches
330- Quick searches without complex logic
331- When you don't need relational rules (inside/has)
332
333### Search with Rules (scan)
334
335YAML rule-based search for complex structural queries:
336
337```bash
338# With rule file
339ast-grep scan --rule my_rule.yml /path/to/project
340
341# With inline rules
342ast-grep scan --inline-rules "id: find-async
343language: javascript
344rule:
345 kind: function_declaration
346 has:
347 pattern: await \$EXPR
348 stopBy: end" /path/to/project
349
350# JSON output
351ast-grep scan --rule my_rule.yml --json /path/to/project
352```
353
354**When to use:**
355
356- Complex structural searches
357- Relational rules (inside, has, precedes, follows)
358- Composite logic (all, any, not)
359- When you need the power of full YAML rules
360
361**Tip:** For relational rules (inside/has), always add `stopBy: end` to ensure complete traversal.
362
363## Tips for Writing Effective Rules
364
365### Always Use stopBy: end
366
367For relational rules, always use `stopBy: end` unless there's a specific reason not to:
368
369```yaml
370has:
371 pattern: await $EXPR
372 stopBy: end
373```
374
375This ensures the search traverses the entire subtree rather than stopping at the first non-matching node.
376
377### Start Simple, Then Add Complexity
378
379Begin with the simplest rule that could work:
380
3811. Try a `pattern` first
3822. If that doesn't work, try `kind` to match the node type
3833. Add relational rules (`has`, `inside`) as needed
3844. Combine with composite rules (`all`, `any`, `not`) for complex logic
385
386### Use the Right Rule Type
387
388- **Pattern**: For simple, direct code matching (e.g., `console.log($ARG)`)
389- **Kind + Relational**: For complex structures (e.g., "function containing await")
390- **Composite**: For logical combinations (e.g., "function with await but not in try-catch")
391
392### Debug with AST Inspection
393
394When rules don't match:
395
3961. Use `--debug-query=cst` to see the actual AST structure
3972. Check if metavariables are being detected correctly
3983. Verify the node `kind` matches what you expect
3994. Ensure relational rules are searching in the right direction
400
401### Escaping in Inline Rules
402
403When using `--inline-rules`, escape metavariables in shell commands:
404
405- Use `\$VAR` instead of `$VAR` (shell interprets `$` as variable)
406- Or use single quotes: `'$VAR'` works in most shells
407
408**Example:**
409
410```bash
411# Correct: escaped $
412ast-grep scan --inline-rules "rule: {pattern: 'console.log(\$ARG)'}" .
413
414# Or use single quotes
415ast-grep scan --inline-rules 'rule: {pattern: "console.log($ARG)"}' .
416```
417
418## Common Use Cases
419
420### Find Functions with Specific Content
421
422Find async functions that use await:
423
424```bash
425ast-grep scan --inline-rules "id: async-await
426language: javascript
427rule:
428 all:
429 - kind: function_declaration
430 - has:
431 pattern: await \$EXPR
432 stopBy: end" /path/to/project
433```
434
435### Find Code Inside Specific Contexts
436
437Find console.log inside class methods:
438
439```bash
440ast-grep scan --inline-rules "id: console-in-class
441language: javascript
442rule:
443 pattern: console.log(\$\$\$)
444 inside:
445 kind: method_definition
446 stopBy: end" /path/to/project
447```
448
449### Find Code Missing Expected Patterns
450
451Find async functions without try-catch:
452
453```bash
454ast-grep scan --inline-rules "id: async-no-trycatch
455language: javascript
456rule:
457 all:
458 - kind: function_declaration
459 - has:
460 pattern: await \$EXPR
461 stopBy: end
462 - not:
463 has:
464 pattern: try { \$\$\$ } catch (\$E) { \$\$\$ }
465 stopBy: end" /path/to/project
466```
467
468## Resources
469
470### references/
471
472Contains detailed documentation for ast-grep rule syntax:
473
474- `rule_reference.md`: Comprehensive ast-grep rule documentation covering atomic rules, relational rules, composite rules, and metavariables
475
476Load these references when detailed rule syntax information is needed.