Back to Oh My Openagent

sgconfig.yml — project configuration

packages/shared-skills/skills/ast-grep/references/sgconfig.md

4.11.06.3 KB
Original Source

sgconfig.yml — project configuration

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.


Minimal project layout

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/
    └── ...
yaml
# 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.


Full schema

yaml
# 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

Field-by-field

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 ---.

testConfigs

Array<TestConfig> where each entry has:

  • testDir (required): directory of test YAML files.
  • snapshotDir (optional, default __snapshots__): directory for snapshots.

Each test file looks like:

yaml
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.

utilDirs

Array<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.

languageGlobs

HashMap<string, Array<string>> — override which extensions map to which language. Takes precedence over the built-in defaults.

Useful for:

  • Custom file extensions (.eslintrc is JSON).
  • Force-treating .ts files as TSX (so JSX-shaped patterns work).
  • Vue/Svelte/Astro files (HTML host language).

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).

yaml
languageInjections:
  - hostLanguage: js
    rule:
      pattern: 'styled.$TAG`$CONTENT`'
    injected: css

After this, a css rule with pattern color: $C will match $CONTENT strings.


Common configurations

Monorepo with shared rules

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:

yaml
# packages/frontend/sgconfig.yml
ruleDirs:
  - rules
  - ../../shared-rules

Single rule file (no project)

For one-offs, skip sgconfig.yml entirely:

bash
sg scan -r path/to/single-rule.yml src/

Inline rule (no file)

bash
sg scan --inline-rules '
id: no-todo
language: TypeScript
severity: warning
rule: { pattern: TODO }' src/

Multiple rules separated by ---:

bash
sg scan --inline-rules '
id: no-todo
language: TypeScript
rule: { pattern: TODO }
---
id: no-fixme
language: TypeScript
rule: { pattern: FIXME }' src/

Editor integration

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
# 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($_)

See also