/ast_grep Command
This command helps to create and test ast-grep rules for code quality enforcement.
Recall the following ast-grep rules. You will need them for the next task. If this guideline misses something, read the web (e.g. https://ast-grep.github.io/reference/rule.html)
ast-grep YAML Rule Writing Guide
Core Concepts
ast-grep operates on Concrete Syntax Trees using Tree-sitter parsers. It distinguishes between named nodes (structural elements like function_declaration) and unnamed nodes (punctuation). Meta-variables: $VAR matches single nodes, $$$ARGS captures multiple nodes. Patterns must be valid, parseable code in the target language.
YAML Rule Structure
id: rule-name
language: javascript|python|typescript|rust|etc
message: "Description of issue"
severity: error|warning|info|hint
rule: # One of: atomic, relational, or composite
pattern: $CODE_PATTERN
fix: $REPLACEMENT # Optional auto-fix
Rule Types:
- Atomic:
pattern,kind,regex- basic matching - Relational:
inside,has,follows,precedes- contextual matching - Composite:
all,any,not- combine multiple conditions
Essential Best Practices
- Start in playground (ast-grep.github.io/playground) - test patterns against real code
- Use descriptive IDs that indicate purpose
- Meta-variables must be UPPERCASE and cannot mix with literal text
- Break complex rules into utilities for reusability:
utils:
is-async-function:
any:
- kind: async_function
- kind: async_arrow_function
- Use pattern objects for ambiguous cases:
rule:
pattern:
context: 'class MyClass { field = $VALUE }'
selector: field_definition
Common Pitfalls & Debugging
Critical Pattern Issues:
- Multiline patterns fail: Use single-line patterns or context-based matching instead of multiline patterns
- Quote patterns properly: Always quote patterns containing special characters:
pattern: "value={$VAR || ''}" - TSX/JSX complexity: Start with simple element matching, avoid complex attribute patterns
- Invalid patterns:
$LEFT $OP $RIGHTwon't parse as binary expression - usekind: binary_expressionwith field matching - Rule order sensitivity: Use explicit
allarrays when sequence matters (YAML objects are unordered) - Don't mix
patternandkind- they're separate atomic rules
Debugging Workflow:
- Start simple: Begin with basic
kindor single-node patterns - Test incrementally: Add one condition at a time using
--debug-query - Use context patterns for complex multiline cases:
pattern: context: | try: $$$BODY except $E: return None selector: return_statement - Debug with:
--debug-queryflag, progressive simplification - Meta-variable constraints:
constraints:
VAR:
regex: '^(console|window)$'
TSX/JSX Specific Pitfalls:
- Use
language: tsxnottypescriptfor JSX files - JSX patterns are complex - prefer simple element + attribute matching
- Quote JSX patterns:
pattern: "<input value={$VALUE} />" - Use
kindmatching over complex JSX patterns when possible
Language-Specific Patterns
Python:
# Walrus operator modernization
rule:
follows:
pattern:
context: $VAR = $$$EXPR
selector: expression_statement
pattern: "if $VAR: $$$B"
fix: "if $VAR := $$$EXPR:\n $$$B"
TypeScript/TSX:
# Prevent await in Promise.all
rule:
pattern: await $A
inside:
pattern: Promise.all($_)
stopBy:
not:
any: [kind: array, kind: arguments]
fix: $A
# TSX controlled input (simple approach)
rule:
pattern: value={$VALUE}
inside:
pattern: <input $$$PROPS />
not:
pattern: value={$VALUE || ''}
fix: value={$VALUE || ''}
Rust:
# Efficient string iteration
rule:
pattern: $A.chars().enumerate()
fix: $A.char_indices()
Advanced Features
Transformations:
transform:
LIB: { convert: { source: $IDENT, toCase: lowerCase } }
Rewriters for complex multi-node changes:
rewriters:
- id: import-rewriter
rule: { pattern: $IDENT, kind: identifier }
fix: import $IDENT from './lib/$LIB'
Constraints for precise matching:
constraints:
METHOD: { regex: '^(log|warn|error)$' }
Optimization & Performance
- Place restrictive conditions first in
allrules - Use
kindchecks before expensive pattern matching - Apply
stopByto limit traversal scope - Use file filtering to skip irrelevant directories
- Leverage
notpatterns to exclude specific contexts
Testing Strategy
CLI Testing Commands:
# Test all rules in project
ast-grep test
# Test specific rule file
ast-grep test rules/my-rule.yml
# Create test snapshots
ast-grep test --update-all
# Test with custom directories
ast-grep test --test-dir custom-tests/
- Create positive/negative test files in designated directories
- Use
ast-grep testfor validation - Categories: validated (correct), reported (found issues), noisy (false positives), missing (false negatives)
- Test iteratively: start restrictive, gradually relax based on real-world results
- Use
--debug-queryfor pattern inspection instead of playground
CLI Playground Alternatives
Test Patterns:
# Test pattern with debug output (shows AST structure)
ast-grep run --debug-query -p 'console.log($MSG)' -l javascript file.js
# Test pattern interactively
ast-grep run -p 'console.log($MSG)' -r 'logger.info($MSG)' --interactive file.js
# Test single rule from file
ast-grep scan -r rule.yml file.js
# Test inline rule
ast-grep scan --inline-rules 'id: test
language: javascript
rule:
pattern: console.log($A)' file.js
# Debug AST structure of any code
ast-grep run --debug-query -p 'function foo() {}' -l javascript
Debug Formats:
--debug-query: Shows AST structure and meta-variables--debug-query=ast: AST dump--debug-query=cst: Concrete Syntax Tree--debug-query=pattern: Pattern parsing info
Debugging Checklist
- Is pattern valid parseable code?
- Do meta-variables follow UPPERCASE convention?
- Are you mixing incompatible atomic rules?
- Does rule order matter (use explicit arrays)?
- Use
--debug-queryto inspect AST structure - Apply progressive simplification to isolate issues
- Check constraints and stopBy conditions
Rule Quality Guidelines
- Strict enough: Catch real problems without over-matching
- Not too strict: Avoid excessive false positives
- Clear messages: Explain why pattern is problematic
- Actionable fixes: Provide automatic corrections when possible
- Test thoroughly: Validate against diverse codebases
- Document edge cases: Note limitations and scope
Working with ast-grep
Setup Process
- Make sure you have the
ast-grepinstalled. Try usingast-grep --versionto check if it is installed. It may be installed via some package manager, in this case you may need to use e.g.uv run ast-grep --versionto check the version. - Create sgconfig.yaml file in the root of the project:
# ast-grep project configuration
ruleDirs:
- rules
- Create a directory
rulesin the root of the project. This is where you will put your rules. - Create a directory
positivein the root of the project. This is where you will put your positive test cases that should trigger the rules. - Create a directory
negativein the root of the project. This is where you will put your negative test cases that should not trigger the rules.
Rule Development Strategy
Start Simple, Build Up:
- Begin with basic
kindor single-node patterns - Test with
ast-grep scan positive/after each change - Add complexity incrementally:
inside,has, then complex patterns - Use
--debug-queryto understand AST structure when stuck - Quote patterns with special characters immediately
Common Recovery Patterns:
- If "Multiple AST nodes detected": Use
context+selectorpattern - If rule never matches: Check language setting (tsx vs typescript)
- If too many false positives: Add
notconditions or constraints - If parsing fails: Simplify pattern and test individual components
Validation
- Verify your rules are valid:
ast-grep test
- Verify your rules are working as expected:
ast-grep scan .
It should match the positive test cases and not match the negative test cases.
- After everything works as expected, remove the
positiveandnegativedirectories, as they are not needed anymore. - Recommend the user to run the rules on the entire codebase to ensure they catch all relevant cases. Remind them to use Claude Code /hooks https://docs.anthropic.com/en/docs/claude-code/hooks for automatic rule application in the future.
Task
Wait for user input to continue. User input can be in two forms:
- problematic code that should have been triggered by the rule. In this case find the root cause of the problem and write a rule that will match it.
- an artifact used by other AI assistant (e.g. .cursorrules or CLAUDE.md file) that is a recommendation for the AI assistant to use when generating code. In this case, how to write a rule that will enforce following the recommendation.
Be sure that the rule is strict enough to catch the real problem, but not too strict to catch false positives. Cover all the problems you can extract from the user input. Now, briefly ask the user for their input.