feat: unwrap search results for human-readable table output#250
Merged
feat: unwrap search results for human-readable table output#250
Conversation
ec890e1 to
babf6aa
Compare
Search run/saved-run commands now display flattened event rows instead of raw SearchResult wrapper objects in table mode. Events from all pages are merged into a single table with smart column selection. Machine-readable formats (json, yaml, csv, jsonl) pass through the raw API response unchanged. Changes: - Unwrap events: flatten mtd + nested data using dot notation - Smart column limiting: max 15 columns, priority fields first, noisy routing metadata dropped. --wide bypasses the limit. - Progress feedback to stderr in interactive mode: query_id on start, page/events/elapsed during pagination, waiting status during polls - Cumulative stats summary using server-aggregated cumulativeStats - Stats from events results only (not facets/timeline) - Clean Ctrl+C: sends DELETE to cancel query on server, prints status - Handle null rows/facets/timeseries in API response (JSON null) - Flush stderr for immediate progress display - --raw flag to get old behavior in table mode - 79 tests covering all new functionality Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
maximelb
previously approved these changes
Mar 13, 2026
babf6aa to
c22f04e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Details
Improves the
search runandsearch saved-runcommands by unwrapping raw API SearchResult wrapper objects into a clean, human-readable table of events. Previously, the table output showed raw API metadata (searchResultId, nextToken, type, stats) with actual event data collapsed into[1546 items], making it unusable for interactive use.Now in table mode, events from all pages are flattened into a single table with smart column selection (priority columns first, noisy routing metadata dropped, capped at 15 columns). Machine-readable formats (json, yaml, csv, jsonl) pass through the raw API response unchanged.
stderr/stdout separation
All progress messages, event counts, and stats summaries are printed to stderr, keeping stdout clean for data. This means
limacharlie search run ... > results.txtcaptures only the table/data, while progress and stats appear in the terminal. This applies to:(1,546 events))New flags
--raw: Show raw API result objects without unwrapping (restores old behavior)--expand: Show each event as a full pretty-printed JSON block with a--- timestamp | stream | event_type ---header, useful for investigating individual events in detailCtrl+C handling
Pressing Ctrl+C during a search automatically sends a DELETE request to cancel the query on the server, freeing resources. A "Canceling search query..." message is shown when in interactive mode.
Before (raw API wrapper objects)
After (unwrapped event table)
After with --expand (full JSON per event)
Changes
limacharlie/commands/search.py_flatten_event_row(): Flattens SearchResultRow into single-level dict with human-readable timestamps_unwrap_search_results(): Separates results by type (events/facets/timeseries), only takes stats from "events" type_format_stats_summary(): Formats stats line with cumulative stats preferred, human-readable byte sizes_limit_event_columns(): Smart column selection - priority columns first, drop noisy routing metadata, cap at 15_format_expanded_events(): Formats each event as a JSON block with timestamp/stream/type header_make_progress_fn(): Returns stderr callback for interactive terminals (TTY + not quiet)_output_search_results(): Routes to table/expand/raw format, prints event count and stats to stderrrunandsaved-runcommands: Added--raw,--expandflags, progress_fn, KeyboardInterrupt handlinglimacharlie/sdk/search.pyexecute(): Addedprogress_fncallback, tracks pages/events/elapsed time_cancel_query(): Sends DELETE on all exit paths (normal, error, Ctrl+C)tests/unit/test_search_output.pyBlast radius / isolation
search runandsearch saved-runCLI commandsSearch.execute()gainsprogress_fnparameter (backward compatible, defaults to None)Performance characteristics
Notable contracts / APIs
Search.execute()now acceptsprogress_fn: Callable[[str], None] | None- callback for progress messagesSearch._cancel_query()- always called in finally block to clean up server resources