Skip to content
Agentmart
Submit a tool

astgrep

Structural code search and rewrite with ast-grep (tree-sitter AST patterns).

Skill by kronael

IndexedUnlicense★ 6
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/kronael-astgrep

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Structural code search and rewrite with ast-grep (tree-sitter AST patterns). NOT for type-aware refactors (use the LSP or language skill), literal search (use ripgrep) or SQL (no grammar).

Use cases: backend, writing

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/astgrep

What's inside

Skill name
astgrep
Description
Structural code search and rewrite with ast-grep (tree-sitter AST patterns). NOT for type-aware refactors (use the LSP or language skill), literal search (use ripgrep) or SQL (no grammar).
License
Unlicense
Instructions
153 lines in SKILL.md · view on GitHub

Files (1)

  • SKILL.md

SKILL.md (first lines)

# astgrep — structural search & rewrite

Search and rewrite code by its tree-sitter AST, not text. A pattern is real
code with meta-variables; matches respect nesting and skip strings/comments.
Methodology + citations: the bundle source repo's `docs/astgrep/research-astgrep.md`
and `docs/astgrep/codex-critique.md` (empirical breakage); upstream docs at
ast-grep.github.io.

ALWAYS invoke the binary as `ast-grep`. NEVER rely on `sg` — it collides with
shadow-utils' `/usr/bin/sg`; `uv tool upgrade ast-grep-cli` recreates the shim.

## When to invoke / NOT

- USE for: "every `console.log` call regardless of args", multi-line
  constructs, a codemod across many files, AST-`kind`/structure queries.
- NOT for semantic edits (scope-aware rename, follow imports, resolve
  overloads) — ast-grep has no types or cross-file symbol table; use the LSP.
- NOT for a trivial literal string — ripgrep is faster.
- Built-in languages here: `go rust python typescript tsx bash`. Use `tsx`
  (not `ts`) for any file with JSX. **SQL is NOT built in** — it needs a
  compiled tree-sitter grammar under `customLanguages` in `sgconfig.yml`
  (a build step, plus `expandoChar` since `$VAR` isn't valid SQL). Don't
  pretend `-l sql` works.

## Commands

`ast-grep run` — ad-hoc, pattern-driven. `ast-grep scan` — runs YAML rules.
Project adoption, the bundled runner and fixture bindings: `lints.md`.

```sh
# search
ast-grep run -p 'console.log($MSG)' -l ts src/
# rewrite: ALWAYS preview (no -U) first, eyeball the diff, then apply
ast-grep run -p 'print($A)' -r 'logging.info($A)' -l py .
ast-grep run -p 'print($A)' -r 'logging.info($A)' -l py -U .   # applies
# rule-based (maintained codemods/lints)
ast-grep scan -r rule.yml --globs '!**/vendor/**' .
```

Useful flags: `-i` interactive confirm, `-U/--update-all` apply all,
`--json[=stream]` machine output, `--debug-query[=ast]` show how a pattern
parsed (reach for this the moment a pattern "should match but doesn't").

## Outline — list symbols before reading

- ALWAYS `ast-grep outline <file>` before reading a whole source file —
  signatures plus member names, about a thirteenth of the bytes (a 20.6 kB TS
  file → 1.5 kB). NEVER Read a large file only to learn what it defines.
- On a directory it prints exported names only; ALWAYS add
  `--items structure` to see private symbols, `--view signatures` for line
  numbers, `--json=compact` for a script.

## Pattern syntax

- `$VAR` — exactly one **named** node. Uppercase/digits/underscore only.
- `$$$` / `$$$ARGS` — zero or more nodes (args, params, statements).
- `$_` (or `$_X`) — single node, **not captured**.
- Reusing a name forces equality: `$A == $A` matches `a == a`, not `a == b`.
- `$$VAR` — matches **unnamed** nodes (operators) that `$VAR` skips.

ALWAYS write a pattern that is **syntactically valid code** in the target
language — `$LEFT $OP $RIGHT` and dangling fragments silently fail to match.
For ambiguous or fragment patterns, use a **pattern object** instead of a bare
string: `context:` (a larger valid snippet) + `selector:` (the node kind to
return), or match by `kind:` directly.

## Rewrite safety — the iron rules

A bare `run -p/-r` **over-matches by construction**. Codex-verified failures
(`codex-critique.md`, cited above):

- `console.log($$$) -> ''` wrecks expressions: `() => console.log(a)` becomes
  `() => `. Delete log *statements* only via a scan rule with
  `inside: { kind: expression_statement }`.
- `$E.unwrap() -> $E?` fires in `let x = value.unwrap();` → invalid Rust.
- `<Old $$$P />` matches only self-closing JSX, never elements with children.

Therefore:

- ALWAYS preview without `-U` (or use `-i`) on a clean git tree first.
  `-U` is **destructive with no undo**.
- ALWAYS scope a real rewrite with `inside`/`has`/`kind` in a `scan` YAML rule
  rather than a broad `run -r` one-liner.
- ALWAYS run the formatter after (`gofmt`/`rustfmt`/`biome format`/`ruff format`).
  ast-grep substitutes nodes; it does **not** reformat or fix indentation.
- ast-grep has no types — review every hunk a rewrite produces.

## YAML rules

```yaml
id: no-console-log
language: TypeScript
rule:
  pattern: console.log($$$A)
  inside: { kind: expression_statement }
fix: ''
```

- Atomic: `pattern`, `kind`, `regex` (Rust regex — **no look-around or
  backrefs**), `nthChild`, `range`.
- Relational: `inside`, `has`, `follows`, `precedes`, each with `stopBy`
  (`neighbor` default | `end` | a rule).
- Composite: `all`, `any`, `not`, `matches` (reference a `utils` rule).
- `constraints` (per meta-var sub-rules), `transform` (derive vars before
  `fix`), `rewriters` (recursive sub-rewrites).

Comments and docstrings — ALWAYS rewrite them with a `kind: comment` rule,
NEVER `sed`, which also rewrites the same text inside strings:

```yaml
id: retag-todo
language: python
rule: { pattern: $C, kind: comment, regex: 'TODO\(old\)' }
transform:
  NEW: { replace: { source: $C, replace: 'TODO\(old\)', by: 'TODO(new)' } }
fix: $NEW
```

- `transform` needs the node captured — keep `pattern: $C` beside `kind`.

From kronael/tools, Unlicense.

More from kronael/tools

Signals

Stars
6
Forks
3
Open issues
0
Last commit (this folder)
Oct 22, 2026 (6 days ago)

From GitHub (fetched Oct 24, 2026).

Details

License
Unlicense
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from kronael

Cite this page

astgrep skill. Agent Mart. https://agentmart.dev/skills/kronael-astgrep (last updated 2026-10-24)