You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Release Notes
Added
groove can load a grammar it was not built with. A language that is not
compiled in — everything except Rust — arrives as a small library you
download and put in a directory, named by the new grammar_dir key or by GROOVE_GRAMMAR_DIR. Nothing is downloaded automatically, and no library is
opened unless [parsers].enabled names the language it belongs to: opening
one runs its initialisers before a single symbol can be inspected, so groove
looks up the file by name from a fixed table rather than reading whatever is
in the directory. A plugin is checked for the ABI version it declares —
first, and on its own, since every other export is read through the signature
that version defines — then for the exports it must have, a tree-sitter
version this build speaks, a tags query that compiles against its own
grammar, a language name a lang: filter can be written against, and exactly
one valid file extension, which must be the one the enabled id stands for. A
library found under one language's name that declares another is refused
rather than registered, so a mispackaged download cannot quietly take a file
type out of the index and put a different one in. See
"Placing a grammar plugin" in docs/clients.md.
Python is the first grammar published this way.groove-grammar-python
is a new release asset — one archive per platform, each with its .sha256,
built from the same four targets as groove itself. Download it, check the
hash, unpack the library into the grammar directory, and add "py" to [parsers].enabled; a Python file is then indexed one definition at a time,
the way a Rust file already was, with class / function / constant in symbol_kind and lang:python among the tags. It is a separate download
rather than a second compiled-in grammar because every language that is
compiled in is paid for by everyone, whether or not they index it — see ADR-0013.
An id that needs a plugin says so, instead of reading as a typo. Writing "py" in [parsers].enabled used to be answered with the list of supported
ids, as if it were misspelled. It now names the file to place and the
directory to place it in — or, on a machine where no such directory can be
determined, names GROOVE_GRAMMAR_DIR instead of a path that does not exist.
A plugin that is present but unusable is refused with its path and the reason.
Changed
A run that cannot succeed still stops before it creates anything. Every
one of the failures above is decided while the parser registry is built,
which happens before the database is opened and before any model is
downloaded — so a missing or broken plugin costs you a message, not a
half-built index.
A groove.toml that groove merely found cannot choose the grammar
directory.grammar_dir joins fastembed_cache_dir, [transport.http].bind and kb_path as a key that an untrusted config does
not get to set, because a grammar plugin is native code loaded into the
process. As with the cache directory, the safe value is applied whether or
not the key is present: omitting it would otherwise be a way to influence the
choice by saying nothing. Naming the config with --config accepts it as
written, as before. Refusing a missing grammar is not affected by trust —
the same failure happens either way.
A groove.toml that groove merely found cannot choose which parsers run
either. Guarding only grammar_dir guarded the wrong half: naming a
language in [parsers].enabled is what causes a plugin to be looked for at
all, and the same key can switch on the formats with the widest input surface
— pdf, xlsx, pptx, docx — that an operator had deliberately left off.
A discovered config now has [parsers] ignored with a warning naming what it
asked for, and the default set, Markdown alone, is used. Unlike the cache and
grammar directories, an absent key needs no substitute: omitting [parsers]
already means Markdown alone, so there is nothing quieter to fall back to. [parsers.code] goes with it, having no parser left to configure. If you keep a groove.toml beside a project and rely on it to index
anything but Markdown, name it — everywhere groove runs, not only when
serving:groove --config ./groove.toml index --kb-path <kb>. This matters
most on index, and not because the new index would merely be incomplete: groove index deletes the documents it did not visit, so a rebuild that
collects only .mdremoves every .txt, PDF, Office document and source
file already indexed. A PostToolUse hook fires on the next edit, so for
anyone using one that is the first thing that happens after upgrading. The rebuild-on-edit.sh recipe now takes GROOVE_CONFIG for this, and the personal deployment recipe names the config on both index and serve; intranet-http already did. A config next to the binary, or one groove service install placed, is trusted as before and needs no change —
and --config naming a file that is not there is an error rather than a
fallback to discovery, so do not add it to a setup that relies on the
binary-side location.
Fixed
One deeply nested source file no longer stalls indexing. Working out the
scope a definition sits in means walking to the root of the syntax tree, and
that walk costs more the deeper the definition is, so the total grows with
the cube of the nesting: a single 10 KB file of mod a{ repeated a thousand
times took 64 seconds to index, and the byte ceiling that was supposed to
bound this never fired because the file was nowhere near 1 MiB. Since rebuild_index holds the embedder and the database for its whole run, one
such file in a knowledge base stopped every request the server had. A file
holding a definition nested under more than 64 syntax-tree ancestors is now
chunked by lines rather than by definition, and tagged parse:too-deep so
the choice is visible to a search. The file still contributes every byte it
has, as ADR-0012
promises; what it loses is the definition metadata. The bound counts
ancestors rather than seconds on purpose — a wall-clock budget would let the
same file produce different chunks on different machines, and those chunks
are the index. Definitions in groove's own sources sit under at most 8
ancestors, so real code has eight times the room it uses. An index built
before this release keeps its old chunks for files whose content has not
changed; groove index --force rebuilds them. See ADR-0014.