| name | ast-grep |
| description | Structural code search, refactoring, and repository lint-rule setup with ast-grep. Use when searching by syntax shape, writing or testing ast-grep rules, configuring sgconfig.yml, enforcing coding standards, selecting rule severity, or adding ast-grep to project checks and CI.
|
| license | MIT |
| metadata | {"version":"2.0.2","author":"Edmund Miller"} |
ast-grep
Use ast-grep for syntax-aware search, rewriting, and tested structural coding standards. Keep formatting, type checking, path-only policy, cross-file value equality, and runtime behavior with their owning tools.
Use code-search when deciding between ast-grep and text search. After selecting ast-grep, use this skill as the source of truth for command syntax, patterns, rewrites, and repository standards.
Choose the right mode
| Need | Mode |
|---|
| Find one syntax shape | ast-grep run --pattern ... --lang ... |
| Preview or apply one rule file | ast-grep scan --rule rule.yml |
| Enforce repository standards | sgconfig.yml + rule and test directories |
| Search literal text, comments, or docs | grep, not ast-grep |
| Follow definitions, references, or types | language server, not ast-grep |
Reuse an existing compiler or linter rule before adding parallel policy.
Search and refactor
Start with the smallest valid AST pattern:
ast-grep run --lang typescript --pattern 'console.log($$$ARGS)' src
ast-grep run --lang python --pattern 'print($$$ARGS)' .
Use metavariables deliberately:
| Syntax | Meaning |
|---|
$VAR | one named AST node |
$$VAR | one named or anonymous node |
$$$ARGS | zero or more nodes |
$_ / $$$ | non-capturing single/sequence matches |
Quote patterns with single quotes so the shell does not expand $.
Inspect how a plausible query pattern is parsed when it misses:
ast-grep run --lang typescript --pattern 'console.log($$$ARGS)' --debug-query=ast src/example.ts
ast-grep run --lang typescript --pattern 'console.log($$$ARGS)' --debug-query=pattern src/example.ts
--debug-query=ast prints the query pattern's AST, not the source file's AST. Use the ast-grep Playground or language-specific tree-sitter tooling when the source tree itself must be inspected.
For a rewrite, preview before applying:
id: replace-console-log
language: TypeScript
rule:
pattern: console.log($$$ARGS)
fix: logger.info($$$ARGS)
ast-grep scan --rule replace-console-log.yml src
ast-grep scan --rule replace-console-log.yml --update-all src
Treat fix as textual substitution, not parsed output. Run the project formatter, type checker, and focused tests after applying a rewrite.
Integrate editors and automation
Start ast-grep's diagnostics-only language server from the project root so it discovers sgconfig.yml, or pass an explicit config:
ast-grep lsp
ast-grep lsp --config path/to/sgconfig.yml
Use structured output for tooling:
ast-grep run --lang typescript --pattern 'console.log($$$ARGS)' --json=stream src
ast-grep scan --json=compact .
Choose pretty for humans, stream for JSON Lines consumers, and compact for a single-line JSON array. Add --include-metadata to scan only when consumers need rule metadata.
Set up repository standards
-
Inspect repository languages, generated/vendor paths, existing linters, package manager, check runner, CI, and local agent instructions.
-
Install ast-grep reproducibly through the repository toolchain. Prefer the ast-grep executable over the sg alias. Pin a version when parser or snapshot stability matters.
-
Confirm every required parser with a literal smoke query. Treat an unsupported-language error as a packaging problem, not a reason to replace structural policy with text search.
-
Create:
sgconfig.yml
ast-grep/
├── rules/
│ └── no-console-log.yml
└── rule-tests/
└── no-console-log-test.yml
-
Configure project discovery:
ruleDirs:
- ast-grep/rules
testConfigs:
- testDir: ast-grep/rule-tests
-
Verify the selected project root and config:
ast-grep scan --inspect summary
scan requires sgconfig.yml. Project discovery starts in the working directory and walks upward; use --config path/to/sgconfig.yml when invoking from elsewhere.
Author lint rules
Start from an observed violation and the smallest rule that separates it from valid code.
id: no-console-log
language: TypeScript
severity: warning
files:
- src/**/*.ts
- src/**/*.tsx
ignores:
- src/generated/**
message: Use the project logger instead of console.log.
note: Replace console.log with the logger appropriate to this module.
rule:
pattern: console.log($$$ARGS)
Require:
- a stable kebab-case
id
- the exact parser
language
- a concise, actionable
message
- a
note when remediation is not obvious
- narrow
files or ignores where the contract is not global
- the least complex rule object that expresses the invariant
Keep files and ignores relative to the sgconfig.yml directory. Never prefix their globs with ./.
Add constraints, relational rules, labels, transform, or fix only when tests prove the simpler rule insufficient. Apply constraints only to single metavariables; they filter after the main rule matches and cannot repair conflicting patterns inside not.
For detailed rule composition, read references/rule-reference.md.
Test every rule
Create a test file whose id exactly matches the rule:
id: no-console-log
valid:
- logger.info('ready')
- console.error('fatal')
invalid:
- console.log('ready')
- console.log(message, context)
Cover:
- plausible
valid code that must not report, preventing noisy matches
- every prohibited
invalid form the rule claims to detect, preventing missing matches
- boundaries such as nesting, alternate syntax, and structural exclusions
valid and invalid cases are source snippets and do not exercise files or ignores. Verify path globs with a temporary project scan or a focused real-tree smoke test containing included and excluded files.
Run detection tests while iterating:
ast-grep test --skip-snapshot-tests
Commit snapshots when diagnostic spans, labels, or messages are part of the contract:
ast-grep test --update-all
ast-grep test
Keep --skip-snapshot-tests in the permanent check only when detection is the complete contract and diagnostic layout is intentionally unstable. Never use interactive snapshot updates in CI.
For a rule regression, add a failing valid or invalid case first, confirm the expected noisy or missing failure, then fix the rule.
Choose severity
| Severity | Policy | Scan behavior |
|---|
error | Established invariant that blocks changes | Non-zero exit when matched |
warning | Actionable standard during adoption | Reports a warning |
info | Migration or informational finding | Reports informationally |
hint | Low-priority guidance; default | Reports a hint |
off | Temporarily disabled configuration | Does not run |
Promote a rule to error only after tests prove low false-positive risk, remediation is actionable, and the baseline is clean or narrowly scoped. Use CLI overrides for staged rollout or CI policy:
ast-grep scan --error=no-console-log
ast-grep scan --off=no-console-log
ast-grep scan --inspect entity
Control suppressions
Prefer narrow, rule-specific suppression comments with a nearby reason:
console.log(message);
Avoid bare ast-grep-ignore, which suppresses every diagnostic. After verifying version support and cleaning the baseline, enforce explicit IDs and stale-suppression cleanup:
ast-grep scan --error=no-suppress-all --error=unused-suppression
Integrate project checks
Run rule tests before scanning the real tree:
ast-grep test --skip-snapshot-tests
ast-grep scan
Add both commands to the existing local check runner and CI rather than creating a parallel validation path. Keep local and CI config, binary version, working directory, and severity overrides identical.
Prove the complete setup:
- Run the expected binary version from the repository toolchain.
- Load every configured parser with a literal smoke query.
- Pass all rule tests.
- Scan the repository with the intended severity behavior.
- Exercise both commands through the normal project check.
- Update the repository's agent/router documentation so AST rules route to ast-grep and non-AST policy remains with its owning tool.
Avoid failure modes
- Do not create a lint rule without at least one valid and one invalid case.
- Do not use string search disguised as an AST pattern.
- Do not assume a parser is bundled; smoke-test it.
- Do not weaken a rule merely to make a dirty baseline pass; fix, scope, or stage rollout explicitly.
- Do not add broad suppressions to silence false positives; improve the rule and tests.
- Do not duplicate ESLint, Ruff, Clippy, compiler, formatter, or runtime checks.
- Do not rely on a narrowed scan as the only CI gate if the standard is repository-wide.
Official references