Agent skill

ast-grep Codemod Reference

by warp-drive-data in warp-drive-data/warp-drive

Reference for writing and debugging TypeScript and JavaScript codemods with @ast-grep/napi: parsing, node queries, meta-variables, rule objects and editing.

MITAuto-check passedDevelopment

Install ast-grep Codemod Reference

skills CLI
$ npx skills add warp-drive-data/warp-drive --skill ast-grep-codemods -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install warp-drive-data/warp-drive ast-grep-codemods --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/warp-drive-data/warp-drive.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/ast-grep-codemods .claude/skills/ast-grep-codemods && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
ast-grep-codemods
GitHub stars
3.2k
Token cost
~2.6k tokens
SKILL.md length
400 words
Files
1
Skills in repo
3
Repo updated
First seen
Licence
MIT

At a glance

Reference for writing and debugging TypeScript and JavaScript codemods with @ast-grep/napi: parsing, node queries, meta-variables, rule objects and editing.

  • Works in 3 steps: Atomic Rules → Composite Rules → Relational Rules
  • Writing a codemod that rewrites TypeScript or JavaScript with ast-grep
  • SKILL.md covers Parsing, SgNode Core Methods, NapiConfig Rule Object and Rule Types, plus 2 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

The ast-grep rule system used by the schema-migration codemods in the repository's `packages/codemods` project is documented, built on the Node.js binding `@ast-grep/napi`. It shows how to parse source with `parse` and a language, then use `SgNode` methods to search (`find`, `findAll`), traverse (`children`, `parent`, `child`), inspect (`kind`, `text`, `isLeaf`) and extract meta-variable matches.

Edits are made with `replace` and applied in a batch through `commitEdits`. Complex queries use a `NapiConfig` rule object with the atomic rules `kind`, `pattern` and `regex`. In patterns, `$NAME` matches one node, `$$NAME` matches zero or more non-greedily and `$$$NAME` greedily. One gotcha: tree-sitter kind names differ between grammars (`field_definition` for TypeScript, `public_field_definition` or `class_field` elsewhere), so kind lookups should be wrapped in try/catch when trying several.

When your agent uses it

  • Writing a codemod that rewrites TypeScript or JavaScript with ast-grep
  • Debugging a tree-sitter node kind that does not match
  • Working in the packages/codemods directory

Example prompts

  • “Write a codemod that finds every console.log call and replaces it with logger.debug.”
  • “My kind rule for class fields matches nothing in JavaScript files; find out why.”
  • “Collect all class_declaration nodes in the file and print their text.”

Requirements

  • Node.js with the `@ast-grep/napi` package

Workflow steps

3 steps, taken from the step headings in SKILL.md.

  1. Atomic Rules
  2. Composite Rules
  3. Relational Rules

What it can do on your machine

Read from SKILL.md and the folder at commit 34ad57e. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are typescript).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Links to these hosts (documentation or services it may open):

    • ast-grep.github.io

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

ast-grep Codemod Reference loads about 2.6k tokens when it runs. Until then it costs about 62 tokens; SKILL.md has 400 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~62
When it runs · the whole SKILL.md, loaded when a task matches
~2.6k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from warp-drive-data/warp-drive at commit 34ad57e, republished under its MIT licence (© warp-drive-data). 400 words, ~2,566 tokens.

Download SKILL.mdSave it as .claude/skills/ast-grep-codemods/SKILL.md (or your agent's skills folder).
name
ast-grep-codemods
description
ast-grep NAPI reference and patterns for the packages/codemods project. Use when working with @ast-grep/napi in schema-migration codemods or packages/codemods/ directory, writing AST queries, or debugging tree-sitter node matching.
user-invocable
false

ast-grep NAPI Reference for Codemods

This skill provides the ast-grep rule system reference used by packages/codemods/src/schema-migration/. The codemods use @ast-grep/napi (the Node.js binding) to parse and transform TypeScript/JavaScript ASTs.

Parsing

typescript
import { parse, Lang, type SgNode } from '@ast-grep/napi';

const ast = parse(Lang.TypeScript, sourceCode);
const root: SgNode = ast.root();

SgNode Core Methods

typescript
// Find first match (returns null if not found)
node.find(matcher: string | number | NapiConfig): SgNode | null

// Find all matches
node.findAll(matcher: string | number | NapiConfig): SgNode[]

// Boolean checks
node.matches(pattern: string): boolean
node.inside(pattern: string): boolean
node.has(pattern: string): boolean
Traversal
typescript
node.children(): SgNode[]           // Direct children
node.parent(): SgNode | null        // Parent node
node.child(nth: number): SgNode | null
node.field(name: string): SgNode | null  // Named field (e.g., 'name', 'body', 'source')
node.ancestors(): SgNode[]
node.next(): SgNode | null          // Next sibling
node.nextAll(): SgNode[]
node.prev(): SgNode | null          // Previous sibling
node.prevAll(): SgNode[]
Inspection
typescript
node.kind(): string       // Tree-sitter node type (e.g., 'field_definition', 'class_body')
node.text(): string       // Full source text
node.isLeaf(): boolean
node.isNamed(): boolean
node.range(): Range       // { start: Pos, end: Pos } (0-indexed)
Meta-variable Extraction
typescript
// After finding with a pattern containing $VAR or $$$VARS:
node.getMatch('VAR'): SgNode | null
node.getMultipleMatches('VARS'): SgNode[]
Code Editing
typescript
const edit = node.replace('newCode');  // Returns Edit object
const newSource = root.commitEdits([edit1, edit2]);  // Apply batch edits

NapiConfig Rule Object

The find and findAll methods accept a NapiConfig object for complex queries:

typescript
node.findAll({
  rule: { /* rule object */ },
  constraints?: { /* meta-variable constraints */ },
})

Rule Types

1. Atomic Rules

Match individual nodes by their properties.

kind - Match by tree-sitter node type
typescript
// Find all class declarations
root.findAll({ rule: { kind: 'class_declaration' } })

// Common TypeScript/JavaScript kinds:
// class_declaration, class_body, field_definition, method_definition,
// import_statement, identifier, property_identifier, decorator,
// call_expression, member_expression, string, template_string

Gotcha: Not all kind names are valid in all grammars. TypeScript uses field_definition, some JavaScript grammars use public_field_definition or class_field. Wrap in try/catch when iterating over multiple possible kinds.

pattern - Match by code pattern with meta-variables
typescript
// Simple pattern
root.findAll({ rule: { pattern: 'console.log($ARG)' } })

// Pattern with context (for ambiguous syntax like class members)
root.findAll({
  rule: {
    pattern: {
      context: 'class A { $FIELD = $INIT }',
      selector: 'field_definition',
    }
  }
})

Meta-variables:

  • $NAME - matches a single AST node
  • $$NAME - matches zero or more nodes (non-greedy)
  • $$$NAME - matches zero or more nodes (greedy)
regex - Match node text against regex
typescript
// Match identifiers starting with underscore
root.findAll({ rule: { kind: 'identifier', regex: '^_' } })
2. Composite Rules

Combine rules with boolean logic.

all - Every rule must match (AND)
typescript
root.findAll({
  rule: {
    all: [
      { kind: 'call_expression' },
      { pattern: '$OBJ.$METHOD($$$ARGS)' },
    ]
  }
})
any - At least one rule must match (OR)
typescript
root.findAll({
  rule: {
    any: [
      { kind: 'field_definition' },
      { kind: 'public_field_definition' },
      { kind: 'class_field' },
    ]
  }
})
not - Negate a rule
typescript
// Find all identifiers that aren't 'constructor'
root.findAll({
  rule: {
    kind: 'identifier',
    not: { regex: '^constructor$' },
  }
})
matches - Reference a utility rule by ID
typescript
root.findAll({
  rule: { matches: 'is-ember-decorator' },
  utils: {
    'is-ember-decorator': {
      kind: 'decorator',
      has: { pattern: '@$NAME', inside: { kind: 'class_body' } },
    }
  }
})
3. Relational Rules

Filter nodes by their position relative to other nodes in the AST.

inside - Node is contained within a matching ancestor
typescript
// Find field_definition nodes that are DIRECT children of class_body
root.findAll({
  rule: {
    kind: 'field_definition',
    inside: {
      kind: 'class_body',
      stopBy: 'neighbor',  // Only check immediate parent
    }
  }
})
has - Node contains a matching descendant
typescript
// Find class declarations that have a decorator
root.findAll({
  rule: {
    kind: 'class_declaration',
    has: {
      kind: 'decorator',
      stopBy: 'neighbor',  // Only check direct children
    }
  }
})
follows - Node appears after a matching sibling
typescript
// Find nodes that follow a decorator
root.findAll({
  rule: {
    kind: 'field_definition',
    follows: { kind: 'decorator' },
  }
})
precedes - Node appears before a matching sibling
typescript
root.findAll({
  rule: {
    kind: 'decorator',
    precedes: { kind: 'method_definition' },
  }
})
Show full SKILL.md (189 more words)Show less
The stopBy Parameter (Critical)

Controls how far relational rules search. This is the most important parameter for correct queries.

ValueBehavior
'neighbor'(Default) Only checks one level (immediate parent for inside, direct children for has)
'end'Searches all the way (all ancestors for inside, all descendants for has)
{ rule }Stops when a node matching the rule is found (inclusive)

Common pattern: matching only direct class members

typescript
// WRONG: findAll with just kind searches ALL descendants recursively
classBody.findAll({ rule: { kind: 'field_definition' } })
// ^ This picks up nested properties inside object literals!

// RIGHT: Use inside rule with stopBy: 'neighbor' to match direct children only
classBody.findAll({
  rule: {
    kind: 'field_definition',
    inside: { kind: 'class_body', stopBy: 'neighbor' },
  }
})
The field Parameter

Restricts matches to a specific named field position in the parent node.

typescript
// Match only the KEY in a key-value pair, not values that happen to match
root.findAll({
  rule: {
    kind: 'pair',
    has: {
      field: 'key',         // Only match the 'key' field position
      regex: 'prototype',
    }
  }
})

Common tree-sitter fields: name, body, source, key, value, left, right, arguments, decorator, type_annotation.

Patterns Used in This Codebase

Finding direct class members (not nested)
typescript
import { NODE_KIND_CLASS_BODY, NODE_KIND_FIELD_DEFINITION, NODE_KIND_METHOD_DEFINITION } from './code-processing.js';

const DIRECT_CLASS_MEMBER = { inside: { kind: NODE_KIND_CLASS_BODY, stopBy: 'neighbor' } } as const;

// Properties - try multiple kinds since grammar varies
function findPropertyDefinitions(classBody: SgNode): SgNode[] {
  for (const nodeType of ['field_definition', 'public_field_definition', 'class_field']) {
    try {
      const props = classBody.findAll({ rule: { kind: nodeType, ...DIRECT_CLASS_MEMBER } });
      if (props.length > 0) return props;
    } catch {
      // Kind not valid in this grammar
    }
  }
  return [];
}

// Methods
function findMethodDefinitions(classBody: SgNode): SgNode[] {
  return classBody.findAll({ rule: { kind: NODE_KIND_METHOD_DEFINITION, ...DIRECT_CLASS_MEMBER } });
}
Finding import statements and extracting source
typescript
const imports = root.findAll({ rule: { kind: 'import_statement' } });
for (const imp of imports) {
  const source = imp.field('source');     // The string literal after 'from'
  const clause = imp.field('import');     // The import clause (specifiers)
  const sourcePath = source?.text();      // e.g., "'@ember-data/model'"
}
Finding decorators preceding a node
typescript
// Walk backwards through siblings collecting decorator nodes
function collectPrecedingDecorators(node: SgNode): string[] {
  const decorators: string[] = [];
  const siblings = node.parent()?.children() ?? [];
  const idx = siblings.indexOf(node);
  for (let i = idx - 1; i >= 0; i--) {
    const sib = siblings[i];
    if (!sib) continue;
    if (sib.kind() === 'decorator') decorators.unshift(sib.text());
    else if (sib.text().trim() !== '') break;
  }
  return decorators;
}
Finding a class that extends a specific base
typescript
// Find class with heritage clause
const classDecl = root.find({ rule: { kind: 'class_declaration' } });
const heritage = classDecl?.find({ rule: { kind: 'class_heritage' } });
const identifiers = heritage?.findAll({ rule: { kind: 'identifier' } }) ?? [];
const baseClasses = identifiers.map((id) => id.text());
Pattern matching with context for class fields
typescript
// Match decorated class fields like: @attr('string') name;
root.findAll({
  rule: {
    pattern: {
      context: 'class A { @$DECORATOR $FIELD = $VALUE }',
      selector: 'field_definition',
    }
  }
})

Debugging Tips

  1. Use node.kind() liberally - When a rule isn't matching, log the actual kinds: classBody.children().map(c => c.kind())
  2. Try/catch around findAll with rules - Invalid kind names throw at runtime, not compile time
  3. Check stopBy behavior - The default 'neighbor' only searches one level. Use 'end' for recursive search.
  4. Use the ast-grep playground - https://ast-grep.github.io/playground.html to test rules interactively

© warp-drive-data, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .claude/skills/ast-grep-codemods of warp-drive-data/warp-drive.

Open the folder on GitHubat commit 34ad57e

Compare with similar skills

ast-grep Codemod Reference next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

ast-grep Codemod Reference compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
ast-grep Codemod Reference this skillwarp-drive-data/warp-drive3.2k—~2.6kAutomated safety check: PassMIT
Generate Release Notesteambit/bit18k—~2.2kAutomated safety check: PassCustom licence
Convert Internal Package to TypeScriptTryGhost/Ghost56k—~1.2kAutomated safety check: PassMIT
jscpd Code Migration Trackerkucherenko/jscpd6.4k—~5kAutomated safety check: PassMIT
Dinero Best Practicesdinerojs/dinero.js6.8k—~756Automated safety check: PassMIT
Coding Standardskurealnum/dotfiles29017 repos~2.9kAutomated safety check: PassNone

Similar skills

  • Generate comprehensive release notes for Bit from git commits and pull requests.

    18k GitHub stars~2.2k tokensUpdated today
    DevelopmentAuto-check passed
  • Moves a legacy internal Ghost package from JavaScript and CommonJS to TypeScript and ESM in three focused commits that keep git file history intact.

    56k GitHub stars~1.2k tokensUpdated today
    DevelopmentAuto-check passed
  • Measures a code port between languages or frameworks with jscpd's function-level comparison, porting tests before code and tracking what is left unmatched.

    6.4k GitHub stars~5k tokensUpdated today
    DevelopmentAuto-check passed
  • Dinero Best Practices

    dinerojs/dinero.js

    Core best practices for the Dinero.js money library. An agent skill from dinerojs/dinero.js.

    6.8k GitHub stars~756 tokensUpdated 3 days ago
    DevelopmentAuto-check passed
  • Coding Standards

    kurealnum/dotfiles

    Universal coding standards, best practices, and patterns for TypeScript, JavaScript, React, and Node.js development.

    290 GitHub starsUsed in 17 repos~2.9k tokens
    DevelopmentAuto-check passed
  • Removes copy-paste duplication found by jscpd, starting with exact clones and hotspots, then renamed and near-miss copies, using proven refactoring strategies.

    6.4k GitHub stars~2.1k tokensUpdated today
    DevelopmentAuto-check passed

More from warp-drive-data/warp-drive

  • Structural Code Search with ast-grep

    warp-drive-data/warp-drive

    Turns natural-language code queries into ast-grep rules for structural search, testing each rule against an example file before running it on a codebase.

    3.2k GitHub starsUsed in 5 repos~2.4k tokens
    Auto-check passed
  • Mock Cache Check-In

    warp-drive-data/warp-drive

    Ensures `.mock-cache` fixture directories under `tests/*` are staged and committed whenever a change adds or modifies a test that uses…

    3.2k GitHub stars~264 tokensUpdated today
    Auto-check passed

Categories

Questions about ast-grep Codemod Reference

What does ast-grep Codemod Reference do?

Reference for writing and debugging TypeScript and JavaScript codemods with @ast-grep/napi: parsing, node queries, meta-variables, rule objects and editing. js binding `@ast-grep/napi`. It shows how to parse source with `parse` and a language, then use `SgNode` methods to search (`find`, `findAll`), traverse (`children`, `parent`, `child`), inspect (`kind`, `text`, `isLeaf`) and extract meta-variable matches.

When should I use ast-grep Codemod Reference?

ast-grep Codemod Reference fits situations like: writing a codemod that rewrites TypeScript or JavaScript with ast-grep; debugging a tree-sitter node kind that does not match; working in the packages/codemods directory.

How do I install ast-grep Codemod Reference in Claude Code?

Run `npx skills add warp-drive-data/warp-drive --skill ast-grep-codemods -a claude-code`. Or copy the skill folder (.claude/skills/ast-grep-codemods in warp-drive-data/warp-drive) into .claude/skills/ast-grep-codemods in your project. Claude Code loads it when a task matches its description.

How do I install ast-grep Codemod Reference in Codex?

Run `npx skills add warp-drive-data/warp-drive --skill ast-grep-codemods -a codex`. Or copy the skill folder (.claude/skills/ast-grep-codemods in warp-drive-data/warp-drive) into .agents/skills/ast-grep-codemods in your project. Codex loads it when a task matches its description.

Can I use ast-grep Codemod Reference in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add warp-drive-data/warp-drive --skill ast-grep-codemods -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/ast-grep-codemods, .gemini/skills/ast-grep-codemods, .github/skills/ast-grep-codemods and .opencode/skills/ast-grep-codemods in your project.

What does ast-grep Codemod Reference need to run?

SKILL.md names no scripts, command-line tools or credentials: ast-grep Codemod Reference is instructions for the agent only. Our summary lists: Node.js with the `@ast-grep/napi` package.

Does ast-grep Codemod Reference access the network?

SKILL.md names 1 domain. As links in the text: ast-grep.github.io. This is read from the text; nothing was executed.

Is ast-grep Codemod Reference safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does ast-grep Codemod Reference use?

ast-grep Codemod Reference is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does ast-grep Codemod Reference use?

About 2.6k tokens (SKILL.md is roughly 10k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to ast-grep Codemod Reference?

Skills that share tags, products or a category with ast-grep Codemod Reference: Generate Release Notes (teambit/bit, 18k stars), Convert Internal Package to TypeScript (TryGhost/Ghost, 56k stars), jscpd Code Migration Tracker (kucherenko/jscpd, 6.4k stars) and Dinero Best Practices (dinerojs/dinero.js, 6.8k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains ast-grep Codemod Reference?

warp-drive-data (a GitHub organization) maintains it in warp-drive-data/warp-drive, which has 3,158 GitHub stars. The repository holds 3 skills in this directory. The repository was last updated on October 9, 2026.

Source: warp-drive-data/warp-drive on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.