From 1bee24c168101a8523f919fb8af112ffb744de53 Mon Sep 17 00:00:00 2001 From: Jonas Haugesen Date: Tue, 4 Aug 2026 11:44:21 +0200 Subject: [PATCH] claude ast-grep skill --- claude/.claude/skills/ast-grep/SKILL.md | 82 +++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 claude/.claude/skills/ast-grep/SKILL.md diff --git a/claude/.claude/skills/ast-grep/SKILL.md b/claude/.claude/skills/ast-grep/SKILL.md new file mode 100644 index 0000000..536fe61 --- /dev/null +++ b/claude/.claude/skills/ast-grep/SKILL.md @@ -0,0 +1,82 @@ +--- +name: ast-grep +description: "Structural code search and rewriting with ast-grep (AST-aware grep). Use instead of rg/grep when searching for code *structure* rather than text: find all calls/definitions matching a shape, match across formatting/line breaks, extract matched sub-parts (function names, arguments), or perform safe codemod-style rewrites. Works for Rust, TS/JS, Python, Go, Java, C/C++, and most tree-sitter languages." +--- + +# ast-grep: structural search & rewrite + +ast-grep matches code by AST shape, not text. Prefer it over `rg` when the target spans lines, when formatting varies, or when you need to rewrite code safely. + +## Pattern syntax + +Patterns are **valid code snippets** with meta-variables: + +| Syntax | Matches | +|---|---| +| `$VAR` | exactly one AST node (UPPERCASE names only) | +| `$$$` / `$$$ARGS` | zero or more nodes (args, params, statements) | +| `$_` | one node, non-capturing | +| repeated `$A` | backreference: `$A == $A` matches `x == x`, not `x == y` | + +## Search + +```bash +ast-grep -p '$X.unwrap()' -l rust src/ # find all .unwrap() calls +ast-grep -p 'fn $NAME($$$) -> $RET { $$$ }' -l rust . # multi-line shapes ok +ast-grep -p 'await $EXPR' -l ts src/ +``` + +- `-l ` is required unless searching by file extension is unambiguous; use it anyway. +- **Always single-quote patterns** — double quotes let the shell eat `$VAR`. +- Pattern must parse as valid code for that language (e.g. a bare `match $E { $$$ }` works in Rust because it's a valid expression). + +## Structured output (for scripting / extraction) + +```bash +ast-grep -p 'pub fn $F($$$) { $$$ }' -l rust src/ --json=compact +``` + +JSON gives `file`, `range`, `text`, and `metaVariables.single.F.text` — use this to extract e.g. all matching function names instead of parsing grep output. + +## Rewrite + +```bash +# Preview as diff (default: prints diff, does NOT modify files) +ast-grep -p '$X.lock().unwrap()' --rewrite '$X.lock().expect("poisoned")' -l rust src/ +# Apply all edits +ast-grep -p '...' --rewrite '...' -l rust src/ --update-all +``` + +Preview first, then `--update-all`. The rewrite is a text template: metavars are substituted, unmatched metavars become empty strings. + +## YAML rules (when patterns aren't enough) + +For context-sensitive matches (inside/has/not), use `scan --inline-rules`: + +```bash +ast-grep scan --inline-rules 'id: unwrap-in-closure +language: rust +rule: + pattern: $X.unwrap() + inside: + kind: closure_expression + stopBy: end +message: unwrap inside closure' src/ +``` + +Rule keys: +- **Atomic:** `pattern`, `kind` (node type, e.g. `closure_expression`), `regex` +- **Relational:** `inside`, `has`, `follows`, `precedes` — add `stopBy: end` to search beyond direct parent/child +- **Composite:** `all`, `any`, `not` +- Optional `fix: