Skip to content

Add diagnostics viewer#6770

Merged
DOsinga merged 3 commits intomainfrom
add-diagnostics-viewer
Jan 28, 2026
Merged

Add diagnostics viewer#6770
DOsinga merged 3 commits intomainfrom
add-diagnostics-viewer

Conversation

@DOsinga
Copy link
Collaborator

@DOsinga DOsinga commented Jan 28, 2026

Summary

TUI to view diagnostics reports (by default from ~/Downloads)

image

Copilot AI review requested due to automatic review settings January 28, 2026 14:58
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 108 to 112
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:
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines 228 to 235
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}"
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines 385 to 392
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()

Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
except Exception as e:
self.name = f"Error loading: {e}"

def get_file_list(self) -> list[str]:
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines 14 to 26
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
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
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
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'reactive' is not used.

Suggested change
from textual.reactive import reactive

Copilot uses AI. Check for mistakes.
# Try to focus a tree if present
tree = self.query_one(JsonTreeView)
tree.focus()
except:
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except block directly handles BaseException.

Suggested change
except:
except Exception:

Copilot uses AI. Check for mistakes.
Comment on lines 323 to 329
except json.JSONDecodeError:
pass

for i in range(1, len(lines)):
try:
responses.append(json.loads(lines[i]))
except json.JSONDecodeError:
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'except' clause does nothing but pass and there is no explanatory comment.

Suggested change
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.

Copilot uses AI. Check for mistakes.
Comment on lines 323 to 329
except json.JSONDecodeError:
pass

for i in range(1, len(lines)):
try:
responses.append(json.loads(lines[i]))
except json.JSONDecodeError:
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'except' clause does nothing but pass and there is no explanatory comment.

Suggested change
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

Copilot uses AI. Check for mistakes.
# Try to focus a tree if present
tree = self.query_one(JsonTreeView)
tree.focus()
except:
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'except' clause does nothing but pass and there is no explanatory comment.

Suggested change
except:
except Exception:
# Intentionally ignore focus errors; non-fatal if no focusable tree exists

Copilot uses AI. Check for mistakes.
@DOsinga DOsinga merged commit 5f82efe into main Jan 28, 2026
16 of 18 checks passed
@DOsinga DOsinga deleted the add-diagnostics-viewer branch January 28, 2026 15:37
eliasposen pushed a commit to eliasposen/goose that referenced this pull request Jan 28, 2026
Co-authored-by: Douwe Osinga <douwe@squareup.com>
Signed-off-by: Elias Posen <elias@posen.ch>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants