ast-grep
- Create example code that represents what you want to match
- Use
--debug-query=cstto understand the actual AST structure - Always add
stopBy: endto relational rules (inside/has) - Test the rule against example code BEFORE searching the codebase
- Escape metavariables in shell commands ($VAR or use single quotes)
Writing rules without testing = false negatives, missed matches, wasted time
This is not optional. Every ast-grep search requires disciplined verification.
ast-grep Code Search
MANDATORY FIRST RESPONSE PROTOCOL
Before writing ANY ast-grep rule, you MUST complete this checklist:
- ☐ Verify ast-grep is installed (run
ast-grep --version) - ☐ If not installed, provide installation instructions and STOP
- ☐ Clarify what the user wants to find (pattern, language, edge cases)
- ☐ Create example code snippet representing the target pattern
- ☐ Use
--debug-query=cstto inspect the AST structure - ☐ Identify the correct
kindvalues for target nodes - ☐ Write the simplest rule that could work (pattern first)
- ☐ Add
stopBy: endto any relational rules (inside/has) - ☐ Test rule against example code with
--stdin - ☐ Announce: "Searching for [pattern description] using [rule type]"
Writing ast-grep rules WITHOUT completing this checklist = missed matches.
Overview
This 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.
Prerequisites Check
CRITICAL: Before proceeding with any ast-grep search, verify ast-grep is installed.
Check if ast-grep is available:
ast-grep --version
If ast-grep is NOT installed:
- Inform the user: "ast-grep is not installed. Would you like installation instructions?"
- If user confirms, provide installation instructions:
macOS (Homebrew):
brew install ast-grep
Linux (Cargo):
cargo install ast-grep
npm (all platforms):
npm install -g @ast-grep/cli
Manual installation:
# Download from GitHub releases
# https://github.com/ast-grep/ast-grep/releases
- After installation, verify with
ast-grep --version - Only proceed with ast-grep search after installation is confirmed
Do NOT attempt to run ast-grep commands if it's not installed.
When to Use This Skill
Use this skill when users:
- Need to search for code patterns using structural matching (e.g., "find all async functions that don't have error handling")
- Want to locate specific language constructs (e.g., "find all function calls with specific parameters")
- Request searches that require understanding code structure rather than just text
- Ask to search for code with particular AST characteristics
- Need to perform complex code queries that traditional text search cannot handle
General Workflow
Follow this process to help users write effective ast-grep rules:
Step 0: Verify Installation (MANDATORY)
Gate: ast-grep installed and verified before proceeding to Step 1.
Before doing anything else, check if ast-grep is installed:
ast-grep --version
If NOT installed:
- Stop immediately
- Inform the user that ast-grep is required
- Provide installation instructions (see Prerequisites Check section above)
- Do NOT proceed until user confirms installation
If installed:
- Note the version for reference
- Proceed to Step 1
Step 1: Understand the Query
Gate: Pattern and language clarified before proceeding to Step 2.
Clearly understand what the user wants to find. Ask clarifying questions if needed:
- What specific code pattern or structure are they looking for?
- Which programming language?
- Are there specific edge cases or variations to consider?
- What should be included or excluded from matches?
Step 2: Create Example Code
Gate: Example code saved to temp file before proceeding to Step 3.
Write a simple code snippet that represents what the user wants to match. Save this to a temporary file for testing.
Example: If searching for "async functions that use await", create a test file:
// test_example.js
async function example() {
const result = await fetchData();
return result;
}
Step 3: Write the ast-grep Rule
Gate: Rule compiles without errors before proceeding to Step 4.
Translate the pattern into an ast-grep rule. Start simple and add complexity as needed.
Key principles:
- Always use
stopBy: endfor relational rules (inside,has) to ensure search goes to the end of the direction - Use
patternfor simple structures - Use
kindwithhas/insidefor complex structures - Break complex queries into smaller sub-rules using
all,any, ornot
Example rule file (test_rule.yml):
id: async-with-await
language: javascript
rule:
kind: function_declaration
has:
pattern: await $EXPR
stopBy: end
See references/rule_reference.md for comprehensive rule documentation.
Step 4: Test the Rule
Gate: Rule matches example code correctly before proceeding to Step 5.
Use ast-grep CLI to verify the rule matches the example code. There are two main approaches:
Option A: Test with inline rules (for quick iterations)
echo "async function test() { await fetch(); }" | ast-grep scan --inline-rules "id: test
language: javascript
rule:
kind: function_declaration
has:
pattern: await \$EXPR
stopBy: end" --stdin
Option B: Test with rule files (recommended for complex rules)
ast-grep scan --rule test_rule.yml test_example.js
Debugging if no matches:
- Simplify the rule (remove sub-rules)
- Add
stopBy: endto relational rules if not present - Use
--debug-queryto understand the AST structure (see below) - Check if
kindvalues are correct for the language
Step 5: Search the Codebase
Gate: Results returned and reviewed before proceeding to Step 6.
Once the rule matches the example code correctly, search the actual codebase:
For simple pattern searches:
ast-grep run --pattern 'console.log($ARG)' --lang javascript /path/to/project
For complex rule-based searches:
ast-grep scan --rule my_rule.yml /path/to/project
For inline rules (without creating files):
ast-grep scan --inline-rules "id: my-rule
language: javascript
rule:
pattern: \$PATTERN" /path/to/project
ast-grep CLI Commands
Inspect Code Structure (--debug-query)
Dump the AST structure to understand how code is parsed:
ast-grep run --pattern 'async function example() { await fetch(); }' \
--lang javascript \
--debug-query=cst
Available formats:
cst: Concrete Syntax Tree (shows all nodes including punctuation)ast: Abstract Syntax Tree (shows only named nodes)pattern: Shows how ast-grep interprets your pattern
Use this to:
- Find the correct
kindvalues for nodes - Understand the structure of code you want to match
- Debug why patterns aren't matching
Example:
# See the structure of your target code
ast-grep run --pattern 'class User { constructor() {} }' \
--lang javascript \
--debug-query=cst
# See how ast-grep interprets your pattern
ast-grep run --pattern 'class $NAME { $$$BODY }' \
--lang javascript \
--debug-query=pattern
Test Rules (scan with --stdin)
Test a rule against code snippet without creating files:
echo "const x = await fetch();" | ast-grep scan --inline-rules "id: test
language: javascript
rule:
pattern: await \$EXPR" --stdin
Add --json for structured output:
echo "const x = await fetch();" | ast-grep scan --inline-rules "..." --stdin --json
Search with Patterns (run)
Simple pattern-based search for single AST node matches:
# Basic pattern search
ast-grep run --pattern 'console.log($ARG)' --lang javascript .
# Search specific files
ast-grep run --pattern 'class $NAME' --lang python /path/to/project
# JSON output for programmatic use
ast-grep run --pattern 'function $NAME($$$)' --lang javascript --json .
When to use:
- Simple, single-node matches
- Quick searches without complex logic
- When you don't need relational rules (inside/has)
Search with Rules (scan)
YAML rule-based search for complex structural queries:
# With rule file
ast-grep scan --rule my_rule.yml /path/to/project
# With inline rules
ast-grep scan --inline-rules "id: find-async
language: javascript
rule:
kind: function_declaration
has:
pattern: await \$EXPR
stopBy: end" /path/to/project
# JSON output
ast-grep scan --rule my_rule.yml --json /path/to/project
When to use:
- Complex structural searches
- Relational rules (inside, has, precedes, follows)
- Composite logic (all, any, not)
- When you need the power of full YAML rules
Tip: For relational rules (inside/has), always add stopBy: end to ensure complete traversal.
Tips for Writing Effective Rules
Always Use stopBy: end
For relational rules, always use stopBy: end unless there's a specific reason not to:
has:
pattern: await $EXPR
stopBy: end
This ensures the search traverses the entire subtree rather than stopping at the first non-matching node.
Start Simple, Then Add Complexity
Begin with the simplest rule that could work:
- Try a
patternfirst - If that doesn't work, try
kindto match the node type - Add relational rules (
has,inside) as needed - Combine with composite rules (
all,any,not) for complex logic
Use the Right Rule Type
- Pattern: For simple, direct code matching (e.g.,
console.log($ARG)) - Kind + Relational: For complex structures (e.g., "function containing await")
- Composite: For logical combinations (e.g., "function with await but not in try-catch")
Debug with AST Inspection
When rules don't match:
- Use
--debug-query=cstto see the actual AST structure - Check if metavariables are being detected correctly
- Verify the node
kindmatches what you expect - Ensure relational rules are searching in the right direction
Escaping in Inline Rules
When using --inline-rules, escape metavariables in shell commands:
- Use
\$VARinstead of$VAR(shell interprets$as variable) - Or use single quotes:
'$VAR'works in most shells
Example:
# Correct: escaped $
ast-grep scan --inline-rules "rule: {pattern: 'console.log(\$ARG)'}" .
# Or use single quotes
ast-grep scan --inline-rules 'rule: {pattern: "console.log($ARG)"}' .
Common Use Cases
Find Functions with Specific Content
Find async functions that use await:
ast-grep scan --inline-rules "id: async-await
language: javascript
rule:
all:
- kind: function_declaration
- has:
pattern: await \$EXPR
stopBy: end" /path/to/project
Find Code Inside Specific Contexts
Find console.log inside class methods:
ast-grep scan --inline-rules "id: console-in-class
language: javascript
rule:
pattern: console.log(\$\$\$)
inside:
kind: method_definition
stopBy: end" /path/to/project
Find Code Missing Expected Patterns
Find async functions without try-catch:
ast-grep scan --inline-rules "id: async-no-trycatch
language: javascript
rule:
all:
- kind: function_declaration
- has:
pattern: await \$EXPR
stopBy: end
- not:
has:
pattern: try { \$\$\$ } catch (\$E) { \$\$\$ }
stopBy: end" /path/to/project
Resources
references/
Contains detailed documentation for ast-grep rule syntax:
rule_reference.md: Comprehensive ast-grep rule documentation covering atomic rules, relational rules, composite rules, and metavariables
Load these references when detailed rule syntax information is needed.
Step 6: Verification (MANDATORY)
After searching the codebase, verify the complete workflow:
Check 1: Rule Matches Example
- Rule matches the example code created in Step 2
- No false negatives on known examples
Check 2: Results Are Valid
- Spot-check 3-5 results to confirm they match intent
- No obvious false positives
Check 3: Edge Cases Covered
- Rule handles variations mentioned by user
- Nested cases handled (if applicable)
Check 4: Rule Is Reproducible
- Rule can be re-run with same results
- Rule file or inline command documented for user
Check 5: Results Reported
- Match count reported to user
- File paths and line numbers provided
- Example matches shown
Gate: Do NOT mark search complete until all 5 checks pass.
Quality Checklist (Must Score 8/10)
Score yourself honestly before marking search complete:
Query Understanding (0-2 points)
- 0 points: Wrote rule without clarifying requirements
- 1 point: Partial clarification (language or pattern, not both)
- 2 points: Full clarification: pattern, language, edge cases, exclusions
Example Code (0-2 points)
- 0 points: No example code created
- 1 point: Example code in memory only (not tested)
- 2 points: Example code saved and verified with --debug-query
Rule Quality (0-2 points)
- 0 points: Rule doesn't compile or match anything
- 1 point: Rule matches but missing stopBy: end on relational rules
- 2 points: Rule is minimal, correct, uses stopBy: end where needed
Testing (0-2 points)
- 0 points: Searched codebase without testing
- 1 point: Tested but didn't verify against example
- 2 points: Tested against example with --stdin, verified matches
Result Validation (0-2 points)
- 0 points: Returned results without checking
- 1 point: Checked count only
- 2 points: Spot-checked results, confirmed they match intent
Minimum passing score: 8/10
Common Rationalizations (All Wrong)
These are excuses. Don't fall for them:
- "I know the AST structure" → STILL use --debug-query=cst to verify
- "It's a simple pattern" → STILL test against example code first
- "stopBy: end slows it down" → STILL add it to relational rules (correctness > speed)
- "The rule compiled" → STILL verify it matches the example
- "I'll check the results later" → Spot-check NOW before returning them
- "This is similar to a previous search" → STILL write fresh example code
- "The user knows what they want" → STILL clarify language and edge cases
- "Inline rules are harder" → Learn to escape $VAR properly, it's not optional
Failure Modes
Failure Mode 1: Missing stopBy: end
Symptom: Relational rule (inside/has) returns no matches even though they exist
Fix: Add stopBy: end to ensure search traverses the entire subtree
Failure Mode 2: Wrong Node Kind
Symptom: Pattern doesn't match even though code looks correct
Fix: Use --debug-query=cst to find the actual kind name (e.g., arrow_function vs function_declaration)
Failure Mode 3: Metavariable Not Detected
Symptom: $VAR appears literally in output instead of matching
Fix: Ensure metavariable is the only content in its AST node; use --debug-query=pattern to verify
Failure Mode 4: Shell Escaping Issues
Symptom: Inline rule fails with "unexpected token" or empty results
Fix: Escape $ as \$ in double quotes, or use single quotes around the pattern
Failure Mode 5: Wrong Language
Symptom: Rule matches nothing in a codebase that clearly has the pattern
Fix: Verify --lang flag matches file extension (e.g., typescript not javascript for .tsx files)
Quick Workflow Summary
STEP 0: VERIFY INSTALLATION (MANDATORY)
├── Run ast-grep --version
├── If NOT installed: provide instructions and STOP
├── If installed: note version and proceed
└── Gate: ast-grep installed and verified
STEP 1: UNDERSTAND THE QUERY
├── What pattern to find?
├── Which programming language?
├── Any edge cases or exclusions?
└── Gate: Pattern and language clarified
STEP 2: CREATE EXAMPLE CODE
├── Write code snippet that should match
├── Save to temp file (test_example.js/ts/py)
├── Run --debug-query=cst to see AST structure
└── Gate: Example code created and AST understood
STEP 3: WRITE THE AST-GREP RULE
├── Start simple: try pattern first
├── If complex: use kind + relational rules
├── Always add stopBy: end to inside/has
├── Combine with all/any/not as needed
└── Gate: Rule compiles without errors
STEP 4: TEST THE RULE
├── Test with --stdin against example code
├── Verify matches are correct
├── Adjust rule if needed
├── Iterate until example matches
└── Gate: Rule matches example correctly
STEP 5: SEARCH THE CODEBASE
├── Run ast-grep scan or run
├── Review results count
├── Spot-check 3-5 results
└── Gate: Results returned and validated
STEP 6: VERIFICATION
├── Check 1: Rule matches example
├── Check 2: Results are valid
├── Check 3: Edge cases covered
├── Check 4: Rule is reproducible
├── Check 5: Results reported
└── Gate: All 5 checks pass
Completion Announcement
When ast-grep search is complete, announce:
ast-grep search complete.
**Quality Score: X/10**
- Query Understanding: X/2
- Example Code: X/2
- Rule Quality: X/2
- Testing: X/2
- Result Validation: X/2
**Search:**
- Pattern: [description]
- Language: [language]
- Rule type: [pattern/kind+relational/composite]
**Results:**
- Matches: [count]
- Files: [file count]
**Example matches:**
[Show 2-3 example file:line matches]
**Rule (for re-use):**
```yaml
[The rule used]
Next steps: [Review matches, refine search, or proceed with refactoring]
---
## Integration with Other Skills
The `ast-grep` skill integrates with:
- **`start`** — Use `start` to identify if ast-grep is the right tool for the search
- **`Grep` tool** — Use Grep for text search; use ast-grep for structural search
- **`Explore` agent** — Use Explore for broad discovery; use ast-grep for precise patterns
**When to use ast-grep vs Grep:**
| Scenario | Tool |
|----------|------|
| Find text "TODO" anywhere | Grep |
| Find function calls with specific structure | ast-grep |
| Find variable names matching regex | Grep |
| Find async functions without try-catch | ast-grep |
| Find imports from specific package | Either (ast-grep more precise) |
| Find code patterns across language constructs | ast-grep |
**Workflow Chain:**
User asks "find X" │ ▼ Is X structural (AST-level)? │ Yes──┼──No │ │ ▼ ▼ ast-grep Grep/Explore
**Escalation Pattern:**
If ast-grep rule becomes too complex (5+ nested conditions), consider:
1. Breaking into multiple simpler searches
2. Using Explore agent for discovery first
3. Asking user to narrow the pattern