Conversation
There was a problem hiding this comment.
Pull request overview
Adds a Textual-based TUI script for browsing and inspecting goose diagnostics zip bundles (defaulting to ~/Downloads).
Changes:
- Introduces
scripts/diagnostics-viewer.py, an interactive diagnostics bundle/session browser. - Adds JSON/JSONL viewing with a collapsible tree and a modal for long string values.
scripts/diagnostics-viewer.py
Outdated
| elif isinstance(value, (int, float)): | ||
| node.add_leaf(f"[cyan]{key}[/cyan]: [yellow]{value}[/yellow]") | ||
| elif isinstance(value, bool): | ||
| node.add_leaf(f"[cyan]{key}[/cyan]: [magenta]{str(value).lower()}[/magenta]") | ||
| elif value is None: |
There was a problem hiding this comment.
bool values will be formatted by the (int, float) branch because bool is a subclass of int, so the later isinstance(value, bool) check is unreachable for booleans; check bool before numeric types in both the dict and list cases.
scripts/diagnostics-viewer.py
Outdated
| def read_file(self, filename: str) -> Optional[str]: | ||
| """Read a file from the zip.""" | ||
| try: | ||
| with zipfile.ZipFile(self.zip_path, 'r') as zf: | ||
| with zf.open(filename) as f: | ||
| return f.read().decode('utf-8', errors='replace') | ||
| except Exception as e: | ||
| return f"Error reading file: {e}" |
There was a problem hiding this comment.
read_file is annotated as returning Optional[str] but it never returns None (errors return an "Error reading file" string), which makes the if content is None: path in update_content dead and mixes error strings into JSON parsing; either return None on missing/failed reads (e.g., catch KeyError) or change the return type to str and adjust callers accordingly.
| def action_search(self): | ||
| """Show search overlay.""" | ||
| overlay = self.query_one(SearchOverlay) | ||
| overlay.display = not overlay.display | ||
| if overlay.display: | ||
| search_input = overlay.query_one("#search-input", Input) | ||
| search_input.focus() | ||
|
|
There was a problem hiding this comment.
action_search toggles a search overlay but there is no handler that actually performs searching or updates #search-results, so Ctrl/Cmd+F presents a non-functional UI; either implement search behavior (e.g., respond to Input.Changed and highlight/move between matches) or remove the overlay and bindings until it's supported.
| except Exception as e: | ||
| self.name = f"Error loading: {e}" | ||
|
|
||
| def get_file_list(self) -> list[str]: |
There was a problem hiding this comment.
list[str] in the return annotation requires Python 3.9+ unless annotations are postponed; since textual supports Python 3.8 in some releases, consider adding from __future__ import annotations at the top or using typing.List[str] to avoid runtime failures on 3.8.
| import json | ||
| import re | ||
| import sys | ||
| import zipfile | ||
| from pathlib import Path | ||
| from typing import Optional, Any | ||
|
|
||
| from textual.app import App, ComposeResult | ||
| from textual.widgets import Header, Footer, Static, Tree, ListView, ListItem, Label, Input | ||
| from textual.containers import Horizontal, Vertical, VerticalScroll, Container | ||
| from textual.binding import Binding | ||
| from textual.reactive import reactive | ||
| from textual.message import Message |
There was a problem hiding this comment.
re and reactive are imported but not used in this file, which adds noise and can confuse future edits; remove unused imports (or wire them up if they were intended for the search feature).
scripts/diagnostics-viewer.py
Outdated
| from textual.widgets import Header, Footer, Static, Tree, ListView, ListItem, Label, Input | ||
| from textual.containers import Horizontal, Vertical, VerticalScroll, Container | ||
| from textual.binding import Binding | ||
| from textual.reactive import reactive |
There was a problem hiding this comment.
Import of 'reactive' is not used.
| from textual.reactive import reactive |
scripts/diagnostics-viewer.py
Outdated
| # Try to focus a tree if present | ||
| tree = self.query_one(JsonTreeView) | ||
| tree.focus() | ||
| except: |
There was a problem hiding this comment.
Except block directly handles BaseException.
| except: | |
| except Exception: |
| except json.JSONDecodeError: | ||
| pass | ||
|
|
||
| for i in range(1, len(lines)): | ||
| try: | ||
| responses.append(json.loads(lines[i])) | ||
| except json.JSONDecodeError: |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| except json.JSONDecodeError: | |
| pass | |
| for i in range(1, len(lines)): | |
| try: | |
| responses.append(json.loads(lines[i])) | |
| except json.JSONDecodeError: | |
| except json.JSONDecodeError: | |
| # Malformed request JSON in diagnostics is tolerated; treat as missing. | |
| pass | |
| for i in range(1, len(lines)): | |
| try: | |
| responses.append(json.loads(lines[i])) | |
| except json.JSONDecodeError: | |
| # Skip individual malformed response lines; show only valid JSON entries. |
| except json.JSONDecodeError: | ||
| pass | ||
|
|
||
| for i in range(1, len(lines)): | ||
| try: | ||
| responses.append(json.loads(lines[i])) | ||
| except json.JSONDecodeError: |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| except json.JSONDecodeError: | |
| pass | |
| for i in range(1, len(lines)): | |
| try: | |
| responses.append(json.loads(lines[i])) | |
| except json.JSONDecodeError: | |
| except json.JSONDecodeError: | |
| # Ignore malformed first line; diagnostics may be truncated or partially written | |
| pass | |
| for i in range(1, len(lines)): | |
| try: | |
| responses.append(json.loads(lines[i])) | |
| except json.JSONDecodeError: | |
| # Ignore malformed response lines; keep displaying any valid entries |
scripts/diagnostics-viewer.py
Outdated
| # Try to focus a tree if present | ||
| tree = self.query_one(JsonTreeView) | ||
| tree.focus() | ||
| except: |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| except: | |
| except Exception: | |
| # Intentionally ignore focus errors; non-fatal if no focusable tree exists |
Co-authored-by: Douwe Osinga <douwe@squareup.com> Signed-off-by: Elias Posen <elias@posen.ch>
Summary
TUI to view diagnostics reports (by default from ~/Downloads)