grove

Structural sight for coding agents

Ask where.Get exact lines.

Agents burn tokens grepping and reading whole files to answer where is this defined, who calls it. grove answers with the exact bytes of one symbol — behind a stable id it reuses across turns.

grove seven structural tools · no model, no network tree-sitter

grove outline src/server.py

  • class Server py:src/server.py#Server@12
  • def __init__ py:src/server.py#Server.__init__@14
  • def handle_request py:src/server.py#Server.handle_request@31
  • def main py:src/server.py#main@88

grove source "py:src/server.py#Server.handle_request"

def handle_request(self, req: Request) -> Response:
    """Processes incoming HTTP requests structurally."""
    handler = self.router.match(req.path)
    return handler(req)
✓ 2 calls one file skeleton one symbol body zero whole-file reads

The whole loop: outline a file to a skeleton of ids, then pull one id's exact bytes. Never the whole file.

The unit of exchange

One line, four facts

Every tool — structural or delegated — answers in the same shape. It is the id you pass to the next call, so an agent can carry a location across turns without re-deriving it.

javascriptgrammar : routes/user.jspath # isLoggedInsymbol @ 50line

Lines are 1-based, the grep -n convention. Pass the whole string back to source to read that symbol's body, or to callers to find its call sites.

The core surface

Seven tools, one working loop

Shaped around what an agent actually does: orient in a repo, find a name, read exactly one thing, trace it, and verify the edit. Every one returns ids you feed to the next.

Orient

grove outline <file>

Generates a file definition skeleton (kind · name · parent · signature · id). Gives the agent structural overview without reading whole files.

grove outline src/server.py

class  Server            12:0   py:src/server.py#Server
  def  __init__          14:4   py:src/server.py#Server.__init__
  def  handle_request    31:4   py:src/server.py#Server.handle_request
def    main              88:0   py:src/server.py#main

Add --json to any command for the agent-facing shape. The same seven are the MCP tool surface, so you and your agent see identically. Full reference: Tools.

Optional second surface

And when you don't know the name yet

The seven tools want a name. When you don't have one — "which files handle billing?" — there's a second, optional surface: grove-explore, a locator backed by a small local model. A project registers one surface or the other, never both; running both puts eight tools in front of your agent with no rule for choosing.

grove init --as mcp default

Structural

The seven tools above. Deterministic, milliseconds, no model and no network. Your agent navigates by name and reads exact byte ranges. Start here — it is what most projects want, and the only surface that costs nothing to run.

  • outline · symbols · source
  • callers · definition · map
  • check

grove init --as mcp-llm

Delegated

One tool. A small local model sweeps the tree with grove's own structural tools plus glob/grep/read, and hands back validated citations. Use it when you don't yet know which file to open.

  • mcp__grove__explore
  • runs on your machine, not an API
  • read the cited lines yourself

Switching modes swaps the registration — grove init strips the surface you left. The delegated mode needs a local inference server; if it isn't running, explore says so in its reply rather than failing silently.

The delegate

grove-explore-base

The locator model, published as GGUF. It is an off-the-shelf Qwen3.5-4B base — self-converted and quantized, adopted as grove's delegate. Not a fine-tune, and the model card says so plainly.

Answer-sheet coverage on the 347-episode holdout, 9 repos, 9 languages.
QuantSizeCoverageRole
Q4_K_M2.78 GB 80.6 default
Q8_04.6 GB 82.1 eval baseline

Serve with thinking on — with it off the model emits empty tool calls. Context 24,576 · temperature 0. Model card →

llama.cpp — serves the GGUF straight from HuggingFace

llama serve -hf entelligentsia/grove-explore-base-GGUF:Q4_K_M

ollama — needs 0.32 or newer

ollama pull bonigopalan/grove-explore-base:q4_k_m

Point grove at it

grove init --as mcp-llm   // opens the config screen

The screen finds a running engine on the usual ports and fills in the endpoint and model for you. Weights live on HuggingFace and ollama; the release surface is in grove-models.

Set up and inspect

You can see what it did

Delegation is only trustworthy if you can audit it. Turn on tracing and every session is recorded — the questions asked, the tools called each turn, the tokens spent, and the answer returned.

The grove-explore config screen: a list of inference engines with llama.cpp on port 8081 selected and marked '1 model', fields for endpoint URL and model name, a tracing toggle switched on, and a checklist of allowed tools — grove, Grep, Glob, Read.
grove-explore config — probes the usual local ports, shows which engines are up and how many models each serves, then fills the endpoint and model for you. Choose which tools the delegate may use, and whether to record sessions.
A table of recorded trace sessions, each row showing time, the client that connected, the model, the steering level, and totals for calls and tokens.
Sessions — every client that connected, with call and token totals. Cost is visible per session, not guessed at.
The calls inside one session: four questions in plain English, each with its turn count, token total, and wall-clock duration.
Calls — the questions as asked, with turns, tokens and wall time. Expensive questions are obvious at a glance.
One call expanded: four turns listing the tools called in each — Grep, Grep, then Read, Read — with request and response detail, followed by the final answer as a list of file-path-symbol-line citations.
One call, unfolded — turn by turn, the tools the delegate chose and what came back, ending in the citations it returned. This is the whole loop, not a summary of it.

Start

Three commands

01

Install

curl -fsSL https://raw.githubusercontent.com/Entelligentsia/grove/main/install.sh | sh

Detects your platform and verifies the checksum. Installs both grove and grove-explore. Homebrew, npm and cargo also work — see Install.

02

Wire it into a project

cd your-project && grove init

Detects your languages, fetches their grammars, pins grove.lock, and registers grove with every coding agent it finds — Claude Code, Cursor, Codex, Gemini CLI, Windsurf, VS Code — each at its own config path, plus a steering note so the agent reaches for grove instead of grep.

03

Ask a where question

Start a fresh session and ask something locational — "where is provision_project defined, and who calls it?" It routes through grove instead of grep. That's the whole setup.

Measured, blind-judged

Same answers, half the context

Three navigation regimes — text search, grove, and an LSP — given the same agent the same prompt across 50 tasks: 10 large real repos × 5 rungs of climbing difficulty. One variable.

Across five rungs of task complexity grove ties on answer quality while carrying about half the context of text search, widening to 2.8 times leaner on the hardest architecture traces.

~2×

leaner on context overall — 395K mean tokens against 780K for text search, at tied answer quality.

2.8×

leaner on the hardest architecture traces, where the lead widens rather than narrows.

L1

is where grove loses. Locating one symbol, plain text search is cheaper — the structural call overhead is fixed.

Full methodology, per-repo data, blind judgements and every raw transcript: is-grep-enough · live dashboard.

Coverage

27 languages, no recompile

Grammars are WASM, fetched at runtime from a hosted registry and pinned in grove.lock. Adding a language is a registry entry, not a new build — so the binary you installed already supports what ships next.

bash · c · c++ · c# · go · java · javascript · julia · php · python · ruby · rust · scala · typescript · tsx · agda · codeql · css · embedded template · haskell · html · jsdoc · json · ocaml · ocaml interface · regex · verilog