Skip to content

1.3.0 - 2026-09-03

Latest

Choose a tag to compare

@github-actions github-actions released this 03 Sep 11:05
3d041f6

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

grooveseek 1.3.0

Download grooveseek 1.3.0

File Platform Checksum
grooveseek-aarch64-apple-darwin.tar.xz Apple Silicon macOS checksum
grooveseek-x86_64-pc-windows-msvc.zip x64 Windows checksum
grooveseek-aarch64-unknown-linux-gnu.tar.xz ARM64 Linux checksum
grooveseek-x86_64-unknown-linux-gnu.tar.xz x64 Linux checksum

groove-tray 1.3.0

Download groove-tray 1.3.0

File Platform Checksum
groove-tray-x86_64-pc-windows-msvc.zip x64 Windows checksum

groove-grammar-python 1.3.0

Download groove-grammar-python 1.3.0

File Platform Checksum
groove-grammar-python-aarch64-apple-darwin.tar.xz Apple Silicon macOS checksum
groove-grammar-python-x86_64-pc-windows-msvc.zip x64 Windows checksum
groove-grammar-python-aarch64-unknown-linux-gnu.tar.xz ARM64 Linux checksum
groove-grammar-python-x86_64-unknown-linux-gnu.tar.xz x64 Linux checksum

groove-svc 1.3.0

Download groove-svc 1.3.0

File Platform Checksum
groove-svc-x86_64-pc-windows-msvc.zip x64 Windows checksum