packages/shared-skills/skills/ast-grep/references/sgconfig.md
sgconfig.yml lives at your project root (the same place as package.json, Cargo.toml, pyproject.toml, etc.) and tells sg scan/sg test where to find rules and tests.
sg walks upward from the current directory until it finds an sgconfig.yml. You can also pass --config <path> explicitly.
my-project/
├── sgconfig.yml
├── rules/
│ ├── no-console.yml
│ └── no-as-any.yml
├── utils/
│ └── is-literal.yml
├── tests/
│ ├── no-console.yml
│ └── __snapshots__/
│ └── no-console-snapshot.yml
└── src/
└── ...
# sgconfig.yml
ruleDirs:
- rules
testConfigs:
- testDir: tests
snapshotDir: __snapshots__
utilDirs:
- utils
That's it. sg scan src/ will load every .yml in rules/, find every .ts/.py/whatever matching the rule's language, and report violations.
# Rule directories — required
ruleDirs:
- rules
- team-rules
- vendor/sg-rules
# Test directories — optional
testConfigs:
- testDir: tests
snapshotDir: __snapshots__
- testDir: integration-tests
# Utility rule directories — optional
# Files here become global utilities accessible via `matches: <id>` from any rule.
utilDirs:
- utils
- team-utils
# Override file-extension -> language mapping — optional
# Useful when your code uses non-standard extensions.
languageGlobs:
html:
- '*.vue'
- '*.svelte'
- '*.astro'
json:
- '.eslintrc'
- '.prettierrc'
cpp:
- '*.c' # treat C as C++
tsx:
- '*.ts' # treat all .ts as TSX (so TSX rules work everywhere)
# Custom tree-sitter languages (experimental) — optional
customLanguages:
mojo:
libraryPath: tree-sitter-mojo.so
extensions: [mojo, '🔥']
expandoChar: _ # Replace $ in patterns when language uses $ syntactically
languageSymbol: tree_sitter_mojo
# Language injection — embedded code in another language (experimental) — optional
# Example: CSS inside styled-components template literals.
languageInjections:
- hostLanguage: js
rule:
pattern: 'styled.$TAG`$CONTENT`'
injected: css
ruleDirs (required)Array<string> — directories containing rule YAML files. Resolved relative to sgconfig.yml.
Each .yml/.yaml file in these directories is loaded as a rule. One file can contain multiple rules separated by ---.
testConfigsArray<TestConfig> where each entry has:
testDir (required): directory of test YAML files.snapshotDir (optional, default __snapshots__): directory for snapshots.Each test file looks like:
id: no-console
valid:
- 'logger.info("hi")'
invalid:
- 'console.log("hi")'
sg test runs every test, compares matches against the snapshot, and fails on diff. Snapshots are created on first run with -U.
utilDirsArray<string> — directories with global utility rules. Each util file must have id and language. Utils become referenceable via matches: <id> from any rule in the project.
languageGlobsHashMap<string, Array<string>> — override which extensions map to which language. Takes precedence over the built-in defaults.
Useful for:
.eslintrc is JSON)..ts files as TSX (so JSX-shaped patterns work).customLanguages (experimental)Register a tree-sitter parser that ast-grep doesn't ship with. Requires:
libraryPath: path to a built .so / .dylib / .dll containing the grammar.extensions: file extensions to recognize.languageSymbol: the C symbol exported by the grammar (typically tree_sitter_<name>).expandoChar (optional): character to substitute for $ in patterns when the host language uses $ syntactically (PHP, jQuery, etc.).This is rarely needed — ast-grep already supports 25 languages out of the box.
languageInjections (experimental)Match patterns inside embedded languages. Example: CSS inside JS template literals (styled-components, emotion).
languageInjections:
- hostLanguage: js
rule:
pattern: 'styled.$TAG`$CONTENT`'
injected: css
After this, a css rule with pattern color: $C will match $CONTENT strings.
monorepo/
├── sgconfig.yml # root config — applies to entire monorepo
├── shared-rules/
│ ├── no-todo.yml
│ └── no-as-any.yml
└── packages/
├── frontend/
│ ├── sgconfig.yml # extends root with frontend-specific rules
│ └── rules/
└── backend/
├── sgconfig.yml # extends root with backend-specific rules
└── rules/
Each package's sgconfig.yml references both the package-local rules and the shared ones:
# packages/frontend/sgconfig.yml
ruleDirs:
- rules
- ../../shared-rules
For one-offs, skip sgconfig.yml entirely:
sg scan -r path/to/single-rule.yml src/
sg scan --inline-rules '
id: no-todo
language: TypeScript
severity: warning
rule: { pattern: TODO }' src/
Multiple rules separated by ---:
sg scan --inline-rules '
id: no-todo
language: TypeScript
rule: { pattern: TODO }
---
id: no-fixme
language: TypeScript
rule: { pattern: FIXME }' src/
VS Code / Neovim / Helix detect sgconfig.yml automatically and surface diagnostics from every rule. Without sgconfig.yml, the LSP runs without any rules loaded.
To enable schema validation in your editor, add a header to each rule file:
# yaml-language-server: $schema=https://raw.githubusercontent.com/ast-grep/ast-grep/main/schemas/rule.json
id: no-console
language: TypeScript
rule:
pattern: console.log($_)
references/yaml-rules.md — rule schema (atomic / relational / composite / transform / fix).references/cli.md — sg scan, sg test, sg new project.