grove-core
The structural code-intelligence library behind the grove
CLI and MCP server. grove-core hosts the tree-sitter AST engine, the
grammar registry, grammar fetch, and source ingest — the same engine
the grove binary drives. It gives you structural, byte-precise, token-cheap
access to a codebase: query definitions, sources, callers, and dependency maps
without reading whole files.
Grammars load at runtime from a hosted WASM registry, so no grammar is
compiled in and adding a language needs no recompile. The crate is clap-free
— command-line concerns live in the grove binary, not here.
Install
Published on crates.io as grove-cst — CST for the concrete syntax trees
tree-sitter builds (the plain grove-core name belongs to an unrelated crate).
The library name is still grove_core, so alias it and your imports stay
unchanged:
[]
= { = "grove-cst", = "0.1" }
Before the first crates.io release, depend on it by git instead:
[]
= { = "https://github.com/Entelligentsia/grove", = "grove-cst" }
Usage
The consumer-facing surface is the ops module — a small set of structural
queries that work for any registered language. Before querying, provision the
grammars for the target project once with init::provision_project: it detects
the project's languages, fetches any missing grammar into the OS cache, and
pins grove.lock. After that, every ops::* call resolves grammars from the
cache.
use Path;
use ;
Offline / pinned registry: to skip the network and resolve grammars from a specific registry root instead, set
GROVE_REGISTRY=<dir>(highest resolution precedence) and call theops::*functions directly — provisioning is only needed to populate the cache.
Manual grammar management
provision_project is the batteries-included path (detect → fetch → lock). If
you want direct control, the fetch and registry modules expose each step:
use Path;
use ;
registry::resolve and for_path return a Grammar (the loaded wasm + compiled
tags.scm + profile), cached per process. registry::search_path() shows the
full resolution precedence, and registry::write_lock / locked_langs read and
write grove.lock for reproducible pins.
The surface
| Function | Returns |
|---|---|
ops::outline |
the definitions in one file (its symbol skeleton) |
ops::symbols |
find symbols across a directory, gitignore-aware |
ops::source |
the full source text of one symbol, by id or name |
ops::check |
the syntactic defects (ERROR / MISSING) in one file |
ops::callers |
every reference to a name, with its enclosing function |
ops::map |
a directory's definitions and their outgoing references |
ops::definition / ops::definition_at |
go-to-def by name or from a use site |
Return types — Symbol, Defect, CallSite, FileMap, MapEntry,
SourceResult — are re-exported at the crate root (e.g. grove_core::Symbol).
init::provision_project is the grammar-provisioning entry point behind
grove init. The lower-level engine, registry, fetch, and ingest modules
are public for hosts that need deeper access.
Every result carries a stable symbol-id (<lang>:<relpath>#<name>@<line>,
1-based) you can pass between calls.
Grammars & the registry
grove-core resolves grammars from the first existing location (precedence):
GROVE_REGISTRY env → <project>/.grove/grammars/ → the OS cache
(~/.cache/grove/grammars on Linux) → a dev registry/ tree. Set
GROVE_REGISTRY to point at a specific registry root for reproducible resolution.
See the grove docs
for the WASM registry, profiles, and the 27 supported languages.
Not an LSP
grove is a syntactic, tree-sitter-powered layer — it parses and locates, it does not do type inference, completion, rename, or type-resolved go-to-def. It is the cheap syntactic layer beneath where an LSP's semantics begin.
License
MIT © the grove authors. Part of the grove project.