| name | ast-grep |
| description | Use this skill when working with ast-grep, an AST-based code search, lint, and rewrite tool. Activate when the user asks to search code patterns, refactor code structurally, create linting rules, or perform AST-based code analysis. Helps with pattern syntax, meta-variables, rule configuration, and CLI commands for Java, JavaScript, TypeScript, Python, Rust, Go, and 20+ other languages. Includes Java-specific patterns for annotations, null checks, exception handling, and Stream API usage. |
ast-grep Skill
You now have access to ast-grep, a powerful AST-based code search, lint, and rewrite tool. Use this skill when working with code analysis, refactoring, or pattern matching tasks.
What is ast-grep?
ast-grep is a command-line tool that searches, lints, and rewrites code using Abstract Syntax Trees (ASTs) rather than plain text. Think of it as "a hybrid of grep, eslint, and codemod."
Key advantages over text-based tools:
- Structurally aware: matches code patterns, not just text
- Language-aware: understands syntax across 20+ languages
- Precise: avoids false positives from string/comment matches
- Fast: written in Rust with multi-core support
Supported languages: C, C++, Rust, Go, Java, Python, C#, JavaScript, TypeScript, HTML, CSS, Kotlin, Swift, JSON, YAML, and more.
Core Commands
1. ast-grep run (Quick searches & rewrites)
ast-grep run -p 'console.log($MSG)' -l javascript
ast-grep run -p 'System.out.println($MSG)' -l java
ast-grep run -p 'var $VAR = $VAL' -r 'let $VAR = $VAL' -l javascript
ast-grep run -p 'new Date()' -r 'LocalDate.now()' -l java
ast-grep run -p 'PATTERN' -r 'REWRITE' -U
ast-grep run -p 'PATTERN' --json
echo "var x = 1" | ast-grep run --stdin -l javascript -p 'var $V = $VAL'
2. ast-grep scan (Rule-based linting)
ast-grep scan
ast-grep scan -r path/to/rule.yml
ast-grep scan --filter 'no-console'
ast-grep scan --inline-rules 'id: test
language: JavaScript
rule:
pattern: console.log($A)'
3. ast-grep test (Validate rules)
ast-grep test
ast-grep test -U
4. ast-grep new (Generate templates)
ast-grep new project
ast-grep new rule my-rule
ast-grep new test my-test
Pattern Syntax
Meta Variables
Meta variables capture AST nodes and enable flexible pattern matching:
Format: $ + uppercase letters/underscores/digits
- Valid:
$VAR, $META_1, $_TEMP, $A
- Invalid:
$invalid, $camelCase, $123
Usage:
Capturing constraints: Reusing the same variable name ensures matched code is identical:
Non-capturing variables: Prefix with _ to match without capturing (performance optimization):
Multi-Node Matching
Use $$$ to match zero or more AST nodes:
Named multi-node:
Pattern Types
1. Code Patterns (most common)
pattern: console.log($MSG)
2. Kind Patterns (match node types)
kind: function_declaration
3. Regex Patterns
regex: "^test_.*"
Rule Configuration
Rules are written in YAML with three essential fields:
id: unique-rule-identifier
language: JavaScript
rule:
pattern: console.log($A)
Rule Categories
1. Atomic Rules - Match single node properties
rule:
pattern: Promise.all($PROMISES)
2. Relational Rules - Match node relationships
rule:
pattern: await $EXPR
inside:
kind: function_declaration
not:
has:
kind: async
3. Composite Rules - Combine multiple rules
rule:
all:
- pattern: $VAR = $VAL
- not:
inside:
kind: function_declaration
any:
- pattern: var $V
- pattern: let $V
Rule Object Fields
| Field | Category | Purpose |
|---|
pattern | Atomic | Match code patterns |
kind | Atomic | Match AST node types |
regex | Atomic | Match with regex |
inside | Relational | Node must be inside matched pattern |
has | Relational | Node must contain matched pattern |
follows | Relational | Node must come after pattern |
precedes | Relational | Node must come before pattern |
all | Composite | All sub-rules must match (AND) |
any | Composite | At least one sub-rule must match (OR) |
not | Composite | Pattern must NOT match |
matches | Utility | Reference other rules by ID |
Advanced Rule Example
id: no-await-in-promise-all
language: TypeScript
message: Avoid await inside Promise.all
note: Use Promise.all to parallelize async operations
severity: warning
rule:
pattern: Promise.all($PROMISES)
has:
pattern: await $_
stopBy: end
fix: |
Remove await from $PROMISES
Java-Specific Patterns
Java has unique syntax features that require special handling in ast-grep. This section covers patterns specifically for Java development.
Working with Annotations
Annotations are a core Java feature but complicate simple pattern matching. Use structural rules with kind and has:
id: find-deprecated-methods
language: Java
rule:
kind: method_declaration
has:
kind: marker_annotation
pattern: "@Deprecated"
id: test-without-assertions
language: Java
rule:
kind: method_declaration
has:
kind: marker_annotation
pattern: "@Test"
not:
has:
any:
- pattern: assert$$$($$$)
- pattern: assertEquals($$$)
- pattern: assertTrue($$$)
message: Test method lacks assertions
Field Declarations with Modifiers
Critical Gotcha: Cannot use meta-variables in modifier positions!
pattern: $MOD String $FIELD;
id: find-string-fields
language: Java
rule:
kind: field_declaration
has:
field: type
regex: ^String$
id: find-list-fields
language: Java
rule:
kind: field_declaration
has:
pattern: List<$T> $NAME
Exception Handling
id: empty-catch-block
language: Java
rule:
kind: catch_clause
has:
pattern: |
catch ($E) {
}
message: Empty catch block - handle or log exception
severity: warning
id: incomplete-try
language: Java
rule:
kind: try_statement
not:
any:
- has:
kind: catch_clause
- has:
kind: finally_clause
message: Try statement must have catch or finally
severity: error
Null Safety Patterns
id: missing-null-check
language: Java
rule:
pattern: $OBJ.$METHOD($$$)
not:
any:
- inside:
pattern: if ($OBJ != null) { $$$ }
- inside:
pattern: if (Objects.nonNull($OBJ)) { $$$ }
- inside:
pattern: if (Objects.requireNonNull($OBJ)) { $$$ }
message: Potential NullPointerException - add null check
note: Consider using Optional or Objects.requireNonNull()
Optional Anti-patterns
id: optional-get-without-check
language: Java
rule:
pattern: $OPT.get()
not:
inside:
any:
- pattern: if ($OPT.isPresent()) { $$$ }
- pattern: $OPT.orElse($$$)
- pattern: $OPT.orElseGet($$$)
- pattern: $OPT.orElseThrow($$$)
message: Optional.get() called without isPresent() check
note: Use orElse(), orElseGet(), or orElseThrow() instead
severity: warning
Stream API Patterns
id: stream-without-terminal
language: Java
rule:
pattern: $LIST.stream().$$$OPS
not:
has:
regex: "\\.(collect|forEach|reduce|count|findFirst|findAny|allMatch|anyMatch|noneMatch|toArray)\\("
message: Stream created but not consumed with terminal operation
id: sequential-stream-on-large-collection
language: Java
rule:
all:
- pattern: $LARGE_LIST.stream().$$$
- has:
regex: "(filter|map|flatMap)"
message: Consider parallelStream() for large collections
note: Profile first to ensure parallel processing benefits outweigh overhead
severity: info
Resource Management
id: use-try-with-resources
language: Java
rule:
all:
- kind: local_variable_declaration
- has:
regex: "(Stream|Connection|Statement|Reader|Writer|InputStream|OutputStream|Scanner|BufferedReader)"
- not:
inside:
kind: resource_specification
message: Resource should be managed with try-with-resources
note: Ensures resources are closed even if exceptions occur
severity: warning
Security Patterns
id: sql-injection-risk
language: Java
rule:
all:
- pattern: $QUERY + $INPUT
- has:
kind: identifier
regex: "(?i)(query|sql|select|insert|update|delete)"
message: Potential SQL injection - use PreparedStatement
note: Never concatenate user input into SQL queries
severity: error
id: hardcoded-credentials
language: Java
rule:
kind: variable_declarator
has:
pattern: $VAR = "$VALUE"
has:
kind: identifier
regex: "(?i)(password|passwd|pwd|secret|key|token|credential)"
message: Hardcoded credential detected
note: Use environment variables or secure configuration
severity: error
Generics and Type Matching
id: raw-type-usage
language: Java
rule:
pattern: List $VAR = new ArrayList()
message: Use generic types - List<Type> instead of raw List
fix: List<Object> $VAR = new ArrayList<>()
Java AST Node Types Reference
Common node types for structural rules:
Declarations:
class_declaration - Class definitions
interface_declaration - Interface definitions
enum_declaration - Enum definitions
record_declaration - Record definitions (Java 14+)
method_declaration - Method definitions
field_declaration - Field/member variable definitions
constructor_declaration - Constructor definitions
local_variable_declaration - Local variable definitions
Statements:
try_statement - Try-catch blocks
try_with_resources_statement - Try-with-resources
if_statement - If conditionals
for_statement - For loops
enhanced_for_statement - For-each loops
while_statement - While loops
synchronized_statement - Synchronized blocks
switch_expression - Switch expressions (Java 12+)
return_statement - Return statements
throw_statement - Throw statements
Expressions:
method_invocation - Method calls
object_creation_expression - New object instantiation
lambda_expression - Lambda expressions
method_reference - Method references (::)
field_access - Field access (obj.field)
array_access - Array indexing
cast_expression - Type casts
instanceof_expression - instanceof checks
ternary_expression - Ternary operator (? :)
Annotations:
annotation - Annotations with values
marker_annotation - Annotations without values (@Override)
Generics:
type_arguments - Generic type arguments
type_parameters - Generic type parameters
wildcard - Generic wildcards (? extends, ? super)
Java-Specific Gotchas
1. Modifier Patterns Don't Work
pattern: public static $TYPE $METHOD($$$)
rule:
kind: method_declaration
regex: "public.*static"
2. Annotations Break Simple Patterns
3. Generic Type Complexity
pattern: Map<String, List<Integer>> $VAR
rule:
kind: local_variable_declaration
has:
field: type
regex: "^Map<"
4. Import Handling
any:
- pattern: "@Test"
- pattern: "@org.junit.Test"
5. Lambda vs Method Syntax
- kind: lambda_expression
- kind: method_reference
Using ast-grep with Claude Code
IMPORTANT: This skill is designed for Claude Code's programmatic usage. Claude cannot use interactive mode.
Recommended Workflow
When using ast-grep through Claude Code, follow this pattern:
1. Search and Analyze
ast-grep run -p 'console.log($A)' -l javascript --json
2. Present Findings to User
Claude should review the JSON output and present findings to the user with:
- What was found
- Locations (file paths and line numbers)
- Proposed changes (if applicable)
3. Apply Changes (Only After User Approval)
ast-grep run -p 'console.log($A)' -r 'logger.info($A)' -l javascript -U
DO NOT use --interactive flag - it requires human input and will fail in Claude Code.
Example Claude Code Workflow
ast-grep run -p 'var $VAR = $VAL' -l javascript --json
ast-grep run -p 'var $VAR = $VAL' -r 'let $VAR = $VAL' -l javascript -U
Best Practices
1. Always Use --json for Analysis
ast-grep run -p 'console.log($A)' -l javascript --json
ast-grep scan --json
2. Use the Right Tool for the Job
- Quick one-off searches:
ast-grep run with --json
- Recurring checks:
ast-grep scan with rules
- Code refactoring:
ast-grep run with --rewrite and -U (after user approval)
- CI/CD integration:
ast-grep scan with JSON output
3. Leverage Relational Rules
Instead of complex regex, use AST relationships:
rule:
pattern: console.log($A)
not:
inside:
pattern: try { $$$ } catch ($E) { $$$ }
4. Test Rules Before Deploying
Always create tests for your rules:
id: no-console-log
testCases:
- id: should-match
match: console.log("test")
- id: should-not-match
match: logger.info("test")
Run: ast-grep test
5. Understand Language-Specific Patterns
Patterns must be valid code in the target language:
pattern: my_function()
pattern: |
if $COND:
$BODY
Common Pitfalls & Solutions
Pitfall 1: Invalid Meta Variable Names
pattern: $myVar = $value
pattern: $MY_VAR = $VALUE
Pitfall 2: Language Mismatch
ast-grep run -p 'print($A)' -l javascript
ast-grep run -p 'console.log($A)' -l javascript
Pitfall 3: Forgetting --lang with stdin
echo "code" | ast-grep run -p 'pattern'
echo "code" | ast-grep run -p 'pattern' --stdin -l python
Pitfall 4: Overly Broad Patterns
pattern: $A
pattern: if ($COND) { $BODY }
Pitfall 5: Not Using stopBy in Relational Rules
rule:
pattern: Promise.all($A)
has:
pattern: await $_
stopBy: end
Useful Flags
| Flag | Purpose | Example | Claude Code Usage |
|---|
-p, --pattern | Search pattern | -p 'console.log($A)' | โ Use |
-r, --rewrite | Replacement code | -r 'logger.info($A)' | โ Use |
-l, --lang | Specify language | -l javascript | โ Use |
--json | Machine-readable output | --json | โ Always use for analysis |
-U, --update-all | Apply all changes | -U | โ Use after user approval |
--stdin | Read from stdin | --stdin | โ Use when needed |
--debug-query | Debug pattern matching | --debug-query | โ Use for troubleshooting |
-j, --threads | Control parallelization | -j 4 | โ Use |
-i, --interactive | Manual review of each change | --interactive | โ DO NOT USE - requires human input |
Integration Examples
With jq (JSON processing)
ast-grep scan --json | jq '.[] | select(.severity == "error")'
With git (changed files only)
git diff --name-only | xargs ast-grep run -p 'pattern'
CI/CD Integration
ast-grep scan --json > results.json
if [ $(jq length results.json) -gt 0 ]; then
exit 1
fi
When to Use ast-grep
โ Use ast-grep for:
- Code refactoring across multiple files
- Finding complex code patterns (nested structures, specific contexts)
- Enforcing code standards (custom linting rules)
- Language-aware code search
- Safe automated code transformations
โ Don't use ast-grep for:
- Simple string searches (use grep/ripgrep)
- Comment/documentation searches
- Binary file searches
- When exact character positions matter more than syntax
Quick Reference
ast-grep run -p 'PATTERN' -l LANG [FILES]
ast-grep run -p 'PATTERN' -r 'REPLACEMENT' -l LANG
ast-grep scan [--rule RULE_FILE]
ast-grep test
ast-grep completions bash > ~/.bash_completion.d/ast-grep
Additional Resources
Workflow for Complex Refactoring (Claude Code)
- Explore: Use
ast-grep run -p 'pattern' --json to find all matches
- Analyze: Parse JSON output and present findings to user
- Test: Create a rule with tests:
ast-grep new rule my-rule
- Apply: After user approval, use
-U to apply all changes
- Validate: Run tests and build to ensure correctness
Remember: ast-grep operates on AST structure, not text. Always think in terms of code syntax, not string patterns.