Skip to content

fix(cli): keep stdout parseable in JSON mode - #76

Merged
lexasub merged 1 commit into
mainfrom
fix/json-output-parseable
Aug 25, 2026
Merged

fix(cli): keep stdout parseable in JSON mode#76
lexasub merged 1 commit into
mainfrom
fix/json-output-parseable

Conversation

@r0h1tb

@r0h1tb r0h1tb commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Description

JSON is the default output for query, goto and callers--humanize is
the opt-out — but every failure path bypassed the formatter and printed a Rich
line to stdout instead:

json.loads(subprocess.check_output(["ast-rag", "goto", name]))

works while the symbol exists and raises JSONDecodeError the moment it does
not. _warn_if_ambiguous breaks it from the other side — its docstring says
the note is returned "for JSON consumers", but it also prints the note to
stdout, ahead of the document and wrapped mid-sentence by Rich:

ambiguous: 'main' matched 2 symbols (2 python). Reporting on a.main. Re-run with
a qualified name to pick another, for example b.main
{
  "target": "a.main",
  ...

Diagnostics now go to a stderr console, empty results go through the formatter
so stdout is still a document, and the ambiguity note is carried in the payload
as ambiguous rather than only as a coloured line.

Exit codes are unchanged — 0 and 1 still separate the cases.

Related Issue

Relates to #15 (--format json everywhere). That issue assumes the commands
have no JSON; three of them already default to it. This fixes the paths where
that default is broken. The remaining flag inconsistency — refs and
symbol-impact take --format table|json and default to table, stats
takes --json — is untouched here and worth its own PR.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature
  • Breaking change
  • Documentation update

Checklist

  • My code follows the code style of this project
  • I have added tests that prove my fix works
  • All new and existing tests passed
  • I have updated the documentation accordingly
  • I have run ast-rag evaluate --all — needs live services; no query or scoring path touched
  • My changes generate no new warnings

Testing

New tests/test_json_output_is_parseable.py drives the real Typer app through
CliRunner with the API mocked, so no Neo4j or Qdrant is needed, and asserts
json.loads(result.stdout) on every empty, not-found and ambiguous path.

Without the fix, 6 of its 10 tests fail:

FAILED test_goto_not_found_still_emits_json
FAILED test_query_no_results_still_emits_json
FAILED test_callers_symbol_not_found_still_emits_json
FAILED test_callers_with_no_callers_still_emits_json
FAILED test_ambiguous_name_keeps_stdout_parseable
FAILED test_ambiguity_note_survives_into_the_json

Two of the ten guard the other direction — --humanize must still produce
Rich text, and the human path must not go silent now that the "not found"
message moved off stdout.

Suite: 257 passed / 1 skipped / 1 xfailed before, 267 / 1 / 1 after (10 new).
Also checked outside CliRunner that err_console really writes to fd 2 in a
normal process, since CliRunner captures streams differently than a pipe.

JSON is the default output for `query`, `goto` and `callers` -- `--humanize`
is the opt-out -- but every failure path bypassed the formatter and printed a
Rich line to stdout instead. `json.loads(subprocess.check_output(...))` worked
while the symbol existed and raised JSONDecodeError the moment it did not.
`_warn_if_ambiguous` broke it from the other side: its docstring says the note
is "for JSON consumers", but it printed the note to stdout ahead of the
document, wrapped mid-sentence by Rich.

Diagnostics now go to a stderr console, empty results go through the
formatter so stdout is still a document, and the ambiguity note is carried in
the payload as `ambiguous` rather than only as a coloured line.

Exit codes are unchanged: 0 and 1 still separate the cases.
@r0h1tb
r0h1tb requested a review from lexasub as a code owner August 24, 2026 20:37
@github-project-automation github-project-automation Bot moved this to Backlog in raged kanban Aug 24, 2026
@lexasub
lexasub merged commit d06ba41 into main Aug 25, 2026
1 check passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in raged kanban Aug 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants