Ast grep
Outline-Driven Development for Claude Code - 46 agents, 25+ skills, diagram-first methodology, AST-based editing, atomic commits.
npx -y skills add OutlineDriven/odin-claude-plugin --skill ast-grepAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
Run code analysis and refactoring with ast-grep. Use when doing AST-based modifications, structural search, linting across code shape, or replacing regex-fragile transformations.
SKILL.md
6.5 KB, as published. Nobody here has run it
ast-grep (sg)
ast-grep is a fast and polyglot tool for code searching, linting, and rewriting based on Abstract Syntax Trees (AST). It excels at structural search and replace where regex fails.
When to use
- Structural Search: Finding code based on structure (e.g., "all function calls to
foowith 2 arguments") regardless of whitespace. - Refactoring: Renaming variables, changing function signatures, or transforming code patterns safely.
- Linting: Creating custom rules to enforce code style or best practices.
- Code Analysis: Extracting information from codebases.
Quick Start
CLI Basics
# Search (pattern must be in single quotes)
ast-grep -p '$A + $B' --lang ts
# Rewrite (dry run)
ast-grep -p '$A != null' --rewrite '$A' --lang ts
# Interactive Rewrite
ast-grep -p 'var $A = $B' --rewrite 'const $A = $B' --interactive
Pattern Syntax
- Meta-variables:
$VARmatches any single node. - Multi-meta-variables:
$$$ARGSmatches zero or more nodes (list of items). - Wildcard:
$_matches any node (non-capturing). - Anonymous:
$$matches any list of nodes (non-capturing).
See Pattern Syntax for details.
The helper: validate first, dry-run first
scripts/ast_grep_helper.py wraps two safety gates:
# Lint a pattern before you search with it (exit 0 valid / exit 2 malformed; flags regex-smell)
python3 scripts/ast_grep_helper.py validate '$A + $B' --lang ts
# Preview a rewrite — diff + "N matches across M files", mutates nothing without --apply
python3 scripts/ast_grep_helper.py replace '$A != null' '$A' --lang ts src/
python3 scripts/ast_grep_helper.py replace '$A != null' '$A' --lang ts src/ --apply
validate compiles the pattern through ast-grep's own parser, so it catches malformed queries in every language (including Go/Python where $ is not an identifier char). replace is two-pass: dry-run prints the blast radius, --apply writes.
What to use, when
structural shape (call/func/class/import shaped like X) → ast-grep
text / regex / filenames / comments / string contents → rg / grep
semantic (types, references, "who calls this symbol") → LSP / compiler
across many repos → search engine, then ast-grep per-repo
ast-grep matches syntax, not bytes.
When a rewrite or search surprises you
Rewrite flow (never skip the dry-run):
searchthe pattern to confirm it matches what you think.replacedry-run: read the diff.- Inspect the
N matches across M filescount; if the blast radius is wrong, stop. - Refine the pattern (tighten meta-vars, add
--lang, add context). - Re-run with
--apply.
0-matches ladder (in order):
python3 scripts/ast_grep_helper.py validate '<pat>' --lang L: is the pattern well-formed?- Check
--lang:tsx≠ts; the wrong dialect silently matches nothing. ast-grep run -p '<pat>' -l L --debug-query=pattern: look forERRORin the dumped query tree.- Inspect the target's actual tree (
--debug-query=aston a known-matching snippet). Your node kinds may differ from your guess. - Reproduce in the online playground.
references/pitfalls.md is the deep field guide. Read it when 0 matches surprises you.
Invariants (do not break)
- Validate before you search: lint the pattern via the helper first.
- Dry-run before you apply: never
--apply(or--update-all) without reading the diff. - Writes are two-pass:
--jsonand--update-allconflict: combine them and--jsonsilently wins, so the write is dropped with no error. Preview with--json, then apply with--update-allseparately. - Single-quote patterns in the shell:
$VARmust reach ast-grep unexpanded. --langis required for stdin`: piped input has no filename to infer the dialect from.- A pattern is code, not regex: switch to rg the moment you'd need
|,.*,\w, or[...]. - Invoke
ast-grep, neversg:sgcollides with thesetgroupsbinary on many systems.
Core Concepts
Understanding Named vs Unnamed nodes and Matching Strictness is crucial for precise patterns.
- Named Nodes:
identifier,function_definition(matched by$VAR). - Unnamed Nodes:
(,),;(skipped by default insmartmode). - Strictness: Control matching precision (
smart,cst,ast,relaxed,signature).
See Core Concepts for details.
Rule Configuration (YAML)
For complex tasks, use YAML configuration files.
id: no-console-log
language: TypeScript
rule:
pattern: console.log($$$ARGS)
inside:
kind: function_declaration
stopBy: end
fix: '' # Remove the log
See Rule Configuration for details.
Advanced Rewriting
ast-grep supports complex transformations (regex replace, case conversion) and rewriters for sub-node transformation.
See Rewriting & Transformations for details.
Project Setup & Testing
For larger projects, organize rules and tests using sgconfig.yml.
- Scaffold:
ast-grep new project - Config:
sgconfig.ymldefines rule and test directories. - Testing: Define
validandinvalidcases to ensure rule accuracy.
See Project Setup & Testing for details.
Utility Rules
Reuse logic with local or global utility rules. Enables recursive matching.
utils:
is-literal:
any: [{kind: string}, {kind: number}]
rule:
matches: is-literal
See Utility Rules for details.
Configuration Reference
Full reference for YAML fields (id, severity, files, ignores) and supported languages.
See Configuration Reference for details.
CLI Reference
Common commands: scan, run, new, test, lsp.
See CLI Reference for details.
Required reading
- Recipes: per-language copy-paste patterns (TS/JS, Python, Rust, Go, Java, Kotlin, C, C++). Read this first when starting a task in a given language.
- Pitfalls: failure-mode field guide (regex-vs-AST, incomplete patterns, the two-pass write, named/unnamed nodes, meta-var naming, stdin/tsx,
sg↔setgroups, and the 0-matches debug ladder). Read this when 0 matches surprises you.