CtrlK
BlogDocsLog inGet started
Tessl Logo

ast-grep

AST-based code search and rewrite via tree-sitter patterns. Use instead of Grep/Edit for structural matching, batch rewrites, or context-aware queries (e.g. "unwrap inside impl blocks").

72

Quality

89%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Passed

No known issues

SKILL.md
Quality
Evals
Security

ast-grep

Matches code by parsed AST, not text. Supports metavariable capture, relational rules, and in-place rewrite.

All commands must be run through mise exec -- since ast-grep is installed via mise:

Commands

# Pattern search
mise exec -- ast-grep run -p '$X.unwrap()' -l rust .

# Rewrite: always preview first, then apply with -U
mise exec -- ast-grep run -p '$X.lock().unwrap()' -l rust .
mise exec -- ast-grep run -p '$X.lock().unwrap()' -r '$X.lock().expect("lock poisoned")' -l rust -U .

# Structural search with YAML rules (for relational/composite logic)
mise exec -- ast-grep scan --inline-rules 'id: name
language: rust
rule:
  pattern: $X.unwrap()
  inside:
    kind: impl_item
    stopBy: end' .

# JSON output for programmatic use
mise exec -- ast-grep run -p '$X.unwrap()' -l rust --json .

# Debug: see how ast-grep parses your pattern or target code
mise exec -- ast-grep run -p 'PATTERN' -l rust --debug-query=cst   # concrete syntax tree
mise exec -- ast-grep run -p 'PATTERN' -l rust --debug-query=pattern # metavar detection

Pattern syntax

SyntaxMeaning
$VARSingle named node. Reuse = must match identically ($A == $A matches x == x only)
$$VARSingle unnamed node (operators, punctuation)
$$$Zero or more nodes (variadic args, statements)
$_ prefixNon-capturing (each occurrence matches independently)

Patterns must be valid parseable code. Bare .unwrap() is an ERROR — use $X.unwrap().

Use --selector KIND to disambiguate patterns that parse as the wrong node type. Provide surrounding context in the pattern and select the sub-node you actually want: mise exec -- ast-grep run -p 'struct S { pub $N: $T }' --selector field_declaration -l rust .

YAML rule structure

Rules combine three categories:

  • Atomic: pattern, kind, regex, nthChild
  • Relational: has, inside, precedes, follows — always add stopBy: end
  • Composite: all, any, not, matches
rule:
  all:
    - kind: function_item                    # atomic: match by node type
    - has:                                   # relational: must contain
        kind: await_expression
        stopBy: end                          # required: search entire subtree
    - not:                                   # composite: exclude
        has:
          kind: try_expression
          stopBy: end

Use constraints in YAML rules to filter metavariable text by regex:

rule:
  pattern: $X.$METHOD()
constraints:
  METHOD:
    regex: "^(unwrap|expect)$"

Key Rust node kinds

Use --debug-query=cst to discover kinds at runtime. Non-obvious mappings:

KindNote
function_itemAll fn declarations (pub, async, const, etc.)
impl_itemBoth inherent and trait impls
method_call_expressionx.foo() — distinct from call_expression (foo())
macro_invocationprintln!(...), vec![...]
await_expressionexpr.await
try_expressionThe ? operator
function_modifiersContains async, unsafe, const keywords
visibility_modifierpub, pub(crate), etc.

Shell escaping

Use single-quoted strings for inline rules to avoid $ expansion. If you must double-quote, escape as \$VAR, \$\$\$.

Repository
gitlabhq/orbit-knowledge-graph
Last updated
Created

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.