-
Notifications
You must be signed in to change notification settings - Fork 163
feat(cli): machine-readable --json output for zg query #115
Description
Motivation
zg query currently emits only human/agent-oriented text (default agent-markdown, or --human). That makes zg hard to use as a backend for scripts and wrapper tools, which need to parse results reliably rather than scrape formatted text. Concretely, a caller today has to regex the #N ... matchedBy=... score=... file:range lines, which is brittle across format changes.
A native --json flag would let tools consume query results directly, the way rg --json, semtools search -j, and similar tools already do.
Why this looks low-risk
From reading the source, the result already exists as a structured object before rendering:
- The CLI render layer is cleanly separated in
src/cli/format/context.ts(formatAgentContextResult,printCliHumanContextResult, ...), all operating on an already-structured result. - Result items already carry the fields a consumer wants, including
score(src/engine/types.ts),matchedBy,rank, file/range, and query-group membership.
So this appears to be an additive serializer at the format layer plus arg parsing, rather than an engine change. Happy to be corrected if there's a reason the internal shape shouldn't be exposed directly.
Proposed behaviour
- Add
--jsontozg query, emitting a single JSON object to stdout: a top-level envelope (query, routes, coverage, groups) with ahitsarray, mirroring how the human output is already grouped. Diagnostics stay on stderr so stdout is pure. - Output format is a single selection among agent-markdown (default),
--human, and--json; passing more than one is an error. - Per-hit fields:
rank,file(relative + absolute),range(start/end lines),matchedBy,score, per-route scores where available,queryGroup, andpreviewhonouring--preview. - Score semantics are labelled explicitly, since hybrid ranking uses RRF (
RRF_K = 60) while the vector route carries a cosine distance, and consumers need to know which they're getting. --rgmode keeps ripgrep's own output rather than being remapped, consistent with the existing "options that replace rg's output format are rejected" stance.
Relationship to #68
Issue #68 (B) notes that failed vector/hybrid searches still print scored rows indistinguishable from real hits, so an Agent/MCP caller cannot tell whether to trust them. A structured output with explicit, labelled scores is a natural foundation for a confidence signal (and, later, an explicit low-confidence / no-strong-match flag) that programmatic callers can threshold on.
I'm happy to open a PR along these lines; flagging here first per CONTRIBUTING so the shape can be confirmed before implementation.