CLI tool
biomejs/gritql avatar
biomejs/gritql

GritQL: a declarative query language for searching, linting and rewriting code

GritQL is a query language for searching, linting, and modifying code.

4,599 stars126 forksRustMIT

At a glance

What is it?
GritQL lets you write a code snippet in backticks, add metavariables for the parts you do not know yet, and grow that into a lint or a rewrite. It is a Rust engine over tree-sitter parsers, and the trade-off is a young CLI with a small release history.
Who is it for?
Adopt GritQL if you already have a migration or a lint rule that grep cannot express and you are willing to pin the CLI version, because the release history is thin: v0.0.3 landed on 2026-03-30 and the repository's Cargo workspace declares 0.5.1. Do not adopt it as a drop-in replacement for an existing ESLint or Semgrep setup, since the README documents no migration path and no rule-format compatibility.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The migration problem GritQL was built around

The README is unusually direct about the gap it targets. A migration starts as a grep search, then accumulates conditions: the right package must be imported, some call sites have no viable rewrite, test files should be skipped. By that point the grep is unmaintainable and the team writes a codemod with something like jscodeshift. The README lists the costs of that jump: exploratory work gets thrown away, you read and write AST node names instead of source code, patterns are not composable across frameworks, and iteration on a large repository is slow. GritQL's answer is to keep the query written as source code. Any code snippet in backticks is a valid query, and the same file that started as `console.log($_)` can end as a rewrite with a where clause attached. The intended user is the engineer who owns a cross-repository refactor or a lint rule that regex cannot express, not someone looking for a general-purpose static analyzer.

How a backtick snippet turns into an AST match

GritQL parses the pattern you write and the files it scans with tree-sitter, which the README names as the parser layer for all target languages. That is why a query can be a literal code fragment: the engine parses your snippet with the same grammar it uses on the target file and matches the resulting subtree. Holes are written as `$name` metavariables, and `$_` is the anonymous form. Rewrites use `=>`, with `.` on the right-hand side meaning deletion. Conditions live in a `where` block. The README shows `$msg <: not within or { ... }`, where `<:` is the match operator and `within` is a containment predicate, so the rule reads as a statement about the syntax tree rather than a sequence of visitor callbacks. Patterns are named and stored in `.grit/grit.yaml`, and the README points to a standard library of patterns that can be pulled in rather than rewritten. The engine is written in Rust, and the README claims query optimization that scales to repositories above 10M lines; that number is the project's own claim and is not something this article can verify.

Installing the Grit CLI and running a first rewrite

The README gives one install path, a shell script served from the documentation site. It does not document a Homebrew formula, a cargo install target, or a Windows installer, so treat the curl script as the supported route.

bash
curl -fsSL https://docs.grit.io/install | bash

After that, `grit --help` should list the available subcommands. The first real use is a search: put the pattern you want in backticks and pass it to `grit apply`. The README's example looks for every `console.log` call, with `$_` standing in for the argument.

bash
grit apply '`console.log($_)`'

The same command becomes a rewrite when you add `=>` and a replacement pattern. Here the captured argument is bound to `$msg` and reused on the right-hand side, so nothing about the call is lost.

bash
grit apply '`console.log($msg)` => `winston.log($msg)`'

To keep the rule, the README writes it into `.grit/grit.yaml` with a name, a severity level, and a where clause that excludes test files. Note that the heredoc writes the file, and the pattern body is a YAML block scalar.

bash
cat << 'EOF' > .grit/grit.yaml
patterns:
  - name: use_winston
    level: error
    body: |
      `console.log($msg)` => `winston.log($msg)` where {
        $msg <: not within or { `it($_, $_)`, `test($_, $_)`, `describe($_, $_)` }
      }
EOF
grit apply use_winston

Once the pattern has a name, `grit check` runs it as a lint. The README links this to CI usage but does not state the exit code behaviour, so confirm that yourself before wiring it into a pipeline.

bash
grit check

Where GritQL is the wrong tool

The release history is the first thing to weigh. The most recent release listed is v0.0.3 from 2026-03-30, preceded by v0.0.2 on 2026-03-22 and an alpha tag from 2025-03-26, while the Cargo workspace in the repository declares version 0.5.1. Those numbers do not line up, and the README does not explain the gap. Anyone pinning a version for a CI job should decide which of those is authoritative before depending on it. The second limit is scope: GritQL matches syntax trees. A rule that depends on type information, cross-file symbol resolution, or runtime behaviour is outside what a tree-sitter parse can tell you, and the README does not claim otherwise. Third, the CLI is the documented entry point. The repository also carries a wasm-bindings crate with a `test:wasm` script and a python directory with a pytest script, but the README does not document a supported JavaScript or Python API, so do not plan around those bindings on the strength of the directory layout alone. Finally, if your team already has a working ESLint or Semgrep configuration, GritQL adds a second rule language rather than replacing the first, and the README offers no migration path between them.

GritQL against jscodeshift and Semgrep

The README names jscodeshift as the tool teams reach for once a migration outgrows grep, and the contrast it draws is about authorship. A jscodeshift codemod is a JavaScript program that walks and rewrites the AST, so the transform is expressed in node types and visitor calls. A GritQL pattern is written in the target language's own syntax, with metavariables for the unknown parts, so the exploratory search and the final rewrite can live in the same file. That is a real difference in day-to-day editing, and it is also a constraint: GritQL gives you a pattern language, not a general programming environment, so anything requiring arbitrary computation over the tree has to be expressed as pattern composition or moved out of the tool. Semgrep is the closer comparison on the linting side, and the difference is the direction of travel. Semgrep grew from a search-and-detect tool, while GritQL's README frames rewriting as the goal and linting as a mode of the same pattern. If your work is detection only, the rewrite machinery is weight you carry without using. The README also notes that patterns can be shared through a module system and a standard library, which is the composability argument against copying codemod fragments between repositories.

Maintenance, licence and what a version bump costs

The repository is not archived, and the last push was on 2026-09-15, which is recent enough that the project is being worked on. That is not the same as a stable release cadence: the release list shows two 0.0.x tags in March 2026 and an alpha tag a year earlier, so a team adopting GritQL should expect to track the CLI rather than sit on a long-term support version. Because patterns are plain text in `.grit/grit.yaml`, the upgrade surface is smaller than it would be for a codemod written against an AST API: the file survives, and the risk sits in whether the pattern semantics still behave the same after an engine change. Pin the CLI version in CI and re-run `grit check` after any bump. On licensing, the README states GritQL is released under the MIT licence, the repository carries a LICENSE file, and package.json declares `"license": "MIT"`. The Cargo workspace lists authors as "Iuvo AI, Inc." and "Grit Contributors", and the homepage points at docs.grit.io, so the project has a commercial entity behind it. Whether that affects your use is a question for your own legal review, not something this article can settle.

Editorial conclusion

Adopt GritQL if you already have a migration or a lint rule that grep cannot express and you are willing to pin the CLI version, because the release history is thin: v0.0.3 landed on 2026-03-30 and the repository's Cargo workspace declares 0.5.1. Do not adopt it as a drop-in replacement for an existing ESLint or Semgrep setup, since the README documents no migration path and no rule-format compatibility. Before committing, verify two things yourself: that the install script at https://docs.grit.io/install resolves on your platform, and that `grit check` fails with a non-zero exit code on a file you deliberately break, because the README describes custom lints but does not document the exit-code contract.

Frequently asked questions

What is GritQL and what is it used for?

GritQL is a declarative query language for searching and modifying source code. The README describes it as a middle ground between a grep search and a full codemod program, and it is used both for exploratory searches and for rewrites and custom lints.

How do I install the Grit CLI?

The README gives one installation command: `curl -fsSL https://docs.grit.io/install | bash`. It does not document any other package manager or platform-specific installer.

Which languages can GritQL target?

The README lists JavaScript/TypeScript, Python, JSON, Java, Terraform, Solidity, CSS, Markdown, YAML, Rust, Go and SQL as target languages. It states that once you learn GritQL you can use it to rewrite any of them.

What does GritQL use to parse code?

The README states that GritQL uses tree-sitter for all language parsers. That is why a plain code snippet in backticks is a valid query: it is parsed with the same grammar as the file being scanned.

Official sources

  1. biomejs/gritql on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/biomejs-gritql.svg)](https://hysenlabs.com/projects/biomejs-gritql)