| name | ast-grep |
| description | Structural code search and refactoring using AST patterns. Use when searching for code patterns (not text), finding deprecated patterns, or systematic refactoring. Supports Nextflow, JavaScript, Python, and more. |
| license | MIT |
| metadata | [object Object] |
ast-grep Skill
ast-grep is a structural code search and rewriting tool. Use it when you need to find or modify code based on its AST structure rather than text patterns.
When to Use ast-grep
Use ast-grep when:
- Searching for code patterns (function calls, imports, specific constructs)
- Refactoring code systematically
- Finding deprecated patterns or anti-patterns
- The pattern involves code structure, not just text
Use grep/ripgrep when:
- Searching for literal strings, comments, or documentation
- Simple text matching is sufficient
Quick Reference
| Task | Command |
|---|---|
| Find pattern | ast-grep run --pattern 'PATTERN' --lang LANG |
| Scan with rule | ast-grep scan --rule file.yaml |
| Debug AST | --debug-query=ast |
| Test pattern match | --debug-query=pattern |
| Use inline rule | --inline-rules 'YAML' |
| Nextflow patterns | Use _VAR instead of $VAR |
Quick Start
Simple Pattern Search
# Find all console.log calls
ast-grep run --pattern 'console.log($$$ARGS)' --lang js
# Find Channel.from() calls in Nextflow
ast-grep run --pattern 'Channel.from(___)' --lang nextflow
Using Rules for Complex Patterns
# Scan with a rule file
ast-grep scan --rule path/to/rule.yaml
# Quick inline rule test
ast-grep scan --inline-rules '
id: test-rule
language: javascript
rule:
pattern: console.log($$$ARGS)
'
Debugging Patterns
# See AST structure of code
ast-grep run --pattern '$$$' --debug-query=ast path/to/file.js
# See how pattern matches
ast-grep run --pattern 'your_pattern' --debug-query=pattern path/to/file.js
Core Concepts
Metavariables
| Syntax | Matches | Example |
|---|---|---|
$VAR |
Single named node | console.$METHOD matches console.log |
$$VAR |
Single node (including anonymous) | $$OP matches operators |
$$$VAR |
Zero or more nodes | func($$$ARGS) matches any args |
_ prefix |
Non-capturing (Nextflow) | _VAR instead of $VAR |
Note: In Nextflow, use _ instead of $ for metavariables (configured via expandoChar in sgconfig.yml).
Rule Structure
id: rule-name
language: javascript # or nextflow, python, etc.
severity: warning # error, warning, hint, off
message: "Human-readable message"
note: |
Additional context and fix suggestions
rule:
pattern: code_pattern_here
Workflow for Writing Rules
- Understand the query - What code pattern are you looking for?
- Create example code - Write a small file with the pattern
- Inspect the AST - Use
--debug-query=astto see structure - Write initial pattern - Start simple, use metavariables
- Test and refine - Use
--debug-query=patternto debug matches - Add constraints - Use relational rules (
inside,has) as needed
Common Patterns
Match function with specific content
rule:
all:
- pattern: function $NAME($$$PARAMS) { $$$ }
- has:
pattern: console.log($$$)
stopBy: end
Match code inside a context
rule:
all:
- pattern: await $PROMISE
- inside:
kind: try_statement
stopBy: end
Match missing pattern (lint for absence)
rule:
all:
- pattern: function $NAME($$$) { $$$ }
- not:
has:
pattern: return $$$
stopBy: end
Key Principles
- Always use
stopBy: endfor relational rules (inside,has,precedes,follows) to search the full subtree - Start simple - Get a basic pattern working before adding complexity
- Escape
$in shell - Use\$VARor single quotes when running from bash - Use
allfor order - When metavariables depend on each other,allprocesses rules in order
Integration Notes
OpenCode Tools
This repository includes nf-ast-grep MCP tools for Nextflow-specific searches:
nf-ast-grep_find_processes- Find all process definitionsnf-ast-grep_find_workflows- Find workflow definitionsnf-ast-grep_find_channels- Find channel factory operationsnf-ast-grep_find_deprecated- Find deprecated patternsnf-ast-grep_lint- Run lint rules on Nextflow codenf-ast-grep_search- Search with custom patterns
Use these tools for Nextflow work instead of raw ast-grep commands when available.
Shell Execution
When running ast-grep from bash:
- Single quotes preserve
$metavariables:ast-grep run --pattern 'console.$METHOD' - Escape in double quotes:
ast-grep run --pattern "console.\$METHOD" - For complex patterns, use
--inline-ruleswith a heredoc or rule files
When to Delegate
- Large searches: Use explore agents for searching across large codebases
- Simple patterns: Run ast-grep directly for quick one-off searches
- Rule development: Use iterative bash commands with
--debug-query
Error Handling
"Pattern not matching expected code"
Cause: Pattern syntax doesn't match AST structure.
Solution:
- Inspect actual AST:
ast-grep run --pattern '$$$' --debug-query=ast file.ext - Check node kinds match what you expect
- Simplify pattern and add constraints incrementally
"ast-grep: command not found"
Cause: ast-grep not installed or not in PATH.
Solution:
# Install via cargo
cargo install ast-grep
# Or via npm
npm install -g @ast-grep/cli
# Or via nix
nix shell nixpkgs#ast-grep
"Language not supported"
Cause: Trying to use an unsupported or unconfigured language.
Solution:
- Check supported languages:
ast-grep --help - For custom languages (like Nextflow), ensure
sgconfig.ymlis present withcustomLanguagesconfigured - See Nextflow Reference for custom language setup
"Missing stopBy in relational rule"
Cause: Relational rules (inside, has, precedes, follows) default to stopBy: neighbor which only checks immediate children.
Solution: Always add stopBy: end to search the full subtree:
rule:
has:
pattern: target_pattern
stopBy: end # Don't forget this!
"Metavariable not captured"
Cause: Using _ prefix makes metavariables non-capturing, or metavariable used before it's defined.
Solution:
- Use
$VAR(or_VARin Nextflow) for capturing - In
allblocks, define metavariables before using them (rules process in order)
Shell escaping issues
Cause: $ in patterns gets interpreted as shell variable.
Solution:
# Use single quotes (preferred)
ast-grep run --pattern 'console.$METHOD'
# Or escape in double quotes
ast-grep run --pattern "console.\$METHOD"
# Or use rule files to avoid shell entirely
ast-grep scan --rule my-rule.yaml
"Rule file not found" or YAML errors
Cause: Invalid YAML syntax or wrong file path.
Solution:
- Validate YAML syntax (check indentation, colons, quotes)
- Use absolute paths or run from correct directory
- Test with
--inline-rulesfirst before creating rule file
Examples
Example 1: Finding and Fixing Deprecated Nextflow Patterns
User request: "Find all uses of the deprecated Channel.from() in our Nextflow pipeline"
Workflow:
# Step 1: Quick search to see scope of problem
ast-grep run --pattern 'Channel.from(___)' --lang nextflow
# Step 2: If many results, use the lint tool for structured output
# (uses existing deprecated-channel-from.yaml rule)
Rule used (deprecated-channel-from.yaml):
id: deprecated-channel-from
language: nextflow
severity: warning
message: "Channel.from() is deprecated in DSL2"
note: |
Use Channel.of() for simple values or Channel.fromList() for lists.
Before: Channel.from(1, 2, 3)
After: Channel.of(1, 2, 3)
rule:
pattern: Channel.from(___)
Result: Found 3 instances in main.nf, updated to use Channel.of().
Example 2: Refactoring Console Logging Across a Codebase
User request: "Replace all console.log calls with our custom logger"
Workflow:
# Step 1: Find all console.log calls
ast-grep run --pattern 'console.log($$$ARGS)' --lang js
# Step 2: Create a rewrite rule
cat > /tmp/replace-console.yaml << 'EOF'
id: replace-console-log
language: javascript
rule:
pattern: console.log($$$ARGS)
fix: logger.info($$$ARGS)
EOF
# Step 3: Preview changes
ast-grep scan --rule /tmp/replace-console.yaml
# Step 4: Apply changes (with --update-all)
ast-grep scan --rule /tmp/replace-console.yaml --update-all
Key insight: The fix field in rules enables automatic refactoring, not just finding.
Example 3: Creating a Custom Linting Rule from Scratch
User request: "Create a rule that warns when async functions don't have error handling"
Workflow:
# Step 1: Create example code to understand the AST
cat > /tmp/example.js << 'EOF'
async function good() {
try {
await fetch('/api');
} catch (e) {
console.error(e);
}
}
async function bad() {
await fetch('/api'); // No try-catch!
}
EOF
# Step 2: Inspect AST structure
ast-grep run --pattern '$$$' --debug-query=ast /tmp/example.js
# Step 3: Write initial pattern for async functions with await
ast-grep run --pattern 'async function $NAME($$$) { $$$ }' /tmp/example.js
# Step 4: Add constraint: must have await but no try statement
cat > /tmp/async-error-handling.yaml << 'EOF'
id: async-needs-error-handling
language: javascript
severity: warning
message: "Async function '$NAME' has await but no try-catch"
note: |
Async functions with await should have error handling.
Wrap await calls in try-catch blocks.
rule:
all:
- pattern: async function $NAME($$$PARAMS) { $$$BODY }
- has:
pattern: await $$$
stopBy: end
- not:
has:
kind: try_statement
stopBy: end
EOF
# Step 5: Test the rule
ast-grep scan --rule /tmp/async-error-handling.yaml /tmp/example.js
Result: Rule correctly flags bad() but not good().
Example 4: Finding Implicit Closure Parameters in Nextflow
User request: "Find closures that use implicit 'it' parameter - we want explicit parameters for readability"
Workflow:
# Use the existing rule via nf-ast-grep tools
# Or run directly:
ast-grep scan --rule config/opencode/ast-grep/rules/implicit-it-closure.yaml
Rule explanation (implicit-it-closure.yaml):
id: implicit-it-closure
language: nextflow
severity: hint
message: "Consider using explicit closure parameter instead of implicit 'it'"
rule:
all:
- kind: closure
- has:
pattern: it
stopBy: end
- not:
has:
kind: closure_parameter
stopBy: end
This uses kind matching (AST node type) combined with has/not has to find closures that reference it but don't declare a parameter.
Example 5: Multi-Pattern Search with Dependencies
User request: "Find all React components that use useState but don't have a useEffect cleanup"
Workflow:
cat > /tmp/missing-cleanup.yaml << 'EOF'
id: missing-useeffect-cleanup
language: typescript
severity: warning
message: "Component uses useState but may be missing useEffect cleanup"
rule:
all:
- kind: function_declaration
- has:
pattern: useState($$$)
stopBy: end
- has:
pattern: useEffect($$$)
stopBy: end
- not:
has:
pattern: |
useEffect(() => {
$$$SETUP
return $$$CLEANUP
}, $$$DEPS)
stopBy: end
EOF
ast-grep scan --rule /tmp/missing-cleanup.yaml src/
Key insight: Complex patterns can combine multiple has and not has clauses to express sophisticated constraints.
References
For detailed syntax and advanced features, see:
- Rule Reference - Complete rule syntax documentation
- Nextflow Reference - Nextflow-specific patterns and examples
Progressive Disclosure
This skill document provides a practical introduction to ast-grep. For deeper understanding:
- Start here - Quick reference and common patterns cover 80% of use cases
- Rule Reference - When you need advanced rule syntax (relational rules, transformations)
- Nextflow Reference - For Nextflow-specific work (custom language config, real examples)
- ast-grep docs - For edge cases: https://ast-grep.github.io/