"Guides use of ast-grep for structural code search and rewrite via the Bash tool. Use when analyzing code structure, finding patterns across a codebase, refactoring, understanding type hierarchies, or exploring unfamiliar code. Triggers: 'find all implementations of', 'refactor', 'rename', 'find usages', 'code structure', 'type definitions', 'interface implementations', large-scale code changes. Do NOT use for simple text search, log searching, or non-code file search."
Install
npx skillscat add sebnow/configs/structural-search Install via the SkillsCat registry.
Structural Search with ast-grep
ast-grep matches code by AST structure, not text.
It returns only matched nodes - no surrounding code, no comments, no noise.
A single ast-grep command replaces Glob + Grep + Read chains
with 80% less output for the same complete result.
Prefer ast-grep over Grep+Read when the question is about code structure
(definitions, call sites, type hierarchies, API surfaces).
Prefer Grep for non-code files, literal strings, or textual patterns.
The output from ast-grep contains the full matched code.
Do not follow up with Read or Grep to see what ast-grep already returned.
Kind-based Search
Finds all AST nodes of a given type, regardless of syntax variations
(generics, extends, decorators, etc.).
Pass rules as JSON to avoid YAML indentation issues.
Always pipe through jq -r '.text' to get full matched content:
ast-grep scan --inline-rules '{"id":"find","language":"LANG","rule":{"kind":"KIND"}}' --json=stream PATH | jq -r '.text'Common node kinds:
- TypeScript:
interface_declaration,class_declaration,function_declaration,type_alias_declaration - Go:
type_declaration,function_declaration,method_declaration - Python:
class_definition,function_definition - Rust:
impl_item,struct_item,trait_item,function_item
Compose rules with inside (parent context) and has (child content):
# All TypeScript interfaces with full bodies
ast-grep scan --inline-rules '{"id":"find","language":"typescript","rule":{"kind":"interface_declaration"}}' --json=stream src/ | jq -r '.text'
# Only exported interfaces (inside export_statement)
ast-grep scan --inline-rules '{"id":"find","language":"typescript","rule":{"kind":"interface_declaration","inside":{"kind":"export_statement"}}}' --json=stream src/ | jq -r '.text'
# Functions containing a specific call
ast-grep scan --inline-rules '{"id":"find","language":"go","rule":{"kind":"function_declaration","has":{"pattern":"log.Fatal($$$)"}}}' --json=stream . | jq -r '.text'Pattern Search
Use run -p for matching specific code shapes:
ast-grep run -p 'PATTERN' -l LANG [PATH]$NAME- matches a single AST node$$$- matches zero or more nodes
Limitation: patterns are syntax-specific.export interface $NAME { $$$ } won't match
interfaces with extends or generics.
Use kind-based search when completeness matters.
# Find all calls to a function
ast-grep run -p 'registerTool($$$)' -l typescript src/
# Method calls on any receiver
ast-grep run -p '$OBJ.addEventListener($$$)' -l typescript
# Go error handling
ast-grep run -p 'if err != nil { $$$ }' -l goStructural Rewrites
Preview first, then apply. Rewrite commands require user confirmation.
# Preview
ast-grep run -p 'oldName($$$ARGS)' -l typescript
# Apply (requires confirmation)
ast-grep run -p 'oldName($$$ARGS)' -r 'newName($$$ARGS)' -l typescript -UStructured Output with jq
# Extract matched names
ast-grep run -p 'export interface $NAME { $$$ }' -l typescript --json=stream \
| jq -r '.metaVariables.single.NAME.text'
# Count matches per file
ast-grep run -p 'registerTool($$$)' -l typescript --json=stream \
| jq -r '.file' | sort | uniq -c | sort -rnCommon Issues
Pattern misses syntax variants
Cause: -p matches literal syntax only.
Solution: Use kind-based search for exhaustive results.
Go qualified calls (pkg.Func) don't match
Cause: Go's tree-sitter grammar parses pkg.Func(args) ambiguously.
Solution: Match the unqualified function name instead.
Debug a pattern
ast-grep run -p 'your pattern' -l typescript --debug-query=pattern