diff --git a/strix/interface/cloud/render.py b/strix/interface/cloud/render.py index e8fb6245..eb7bde66 100644 --- a/strix/interface/cloud/render.py +++ b/strix/interface/cloud/render.py @@ -17,6 +17,16 @@ if TYPE_CHECKING: _MAX_TABLE_COLUMNS = 8 _MAX_CELL_LENGTH = 60 _NARROW_TABLE_WIDTH = 120 +_INTERNAL_COLUMNS = frozenset( + { + "organization_id", + "user_id", + "userId", + "installation_id", + "created_by", + "avatarUrl", + } +) _PREFERRED_KEYS = ( "name", @@ -92,6 +102,9 @@ def emit( if isinstance(data, str): console.print(data) return + if isinstance(data, dict): + _print_detail(console, data) + return console.print_json(json.dumps(data, default=str)) @@ -116,6 +129,7 @@ def _print_table( omit_columns: frozenset[str] = frozenset(), hint: str | None = None, ) -> None: + omit_columns = omit_columns | _INTERNAL_COLUMNS columns: list[str] = [ key for key in _PREFERRED_KEYS @@ -171,6 +185,28 @@ def _print_cards( console.print(prefix + " [dim]ยท[/] ".join(parts), soft_wrap=False) +def _print_detail(console: Console, data: dict[str, Any]) -> None: + """Render one API record as a readable field/value view.""" + keys = [key for key in _PREFERRED_KEYS if key in data and key not in _INTERNAL_COLUMNS] + keys.extend( + key for key in data if key not in keys and key not in _INTERNAL_COLUMNS + ) + table = Table(show_header=False, show_edge=False, box=None, padding=(0, 2)) + table.add_column("field", style="bold cyan", no_wrap=True) + table.add_column("value", overflow="fold") + for key in keys: + value = data.get(key) + if value is None: + continue + if isinstance(value, (dict, list)): + rendered = json.dumps(value, indent=2, default=str) + else: + rendered = _cell(value) + table.add_row(_human_label(key), rendered) + console.print(table) + console.print("[dim]Use --json for the lossless machine-readable record.[/]") + + def _human_label(column: str) -> str: labels = { "repository_full_name": "repo", diff --git a/tests/test_cloud_cli.py b/tests/test_cloud_cli.py index f420852f..4723f9f0 100644 --- a/tests/test_cloud_cli.py +++ b/tests/test_cloud_cli.py @@ -727,6 +727,36 @@ def test_pr_review_human_list_prioritizes_actionable_fields( assert value not in output +def test_human_get_prioritizes_details_and_hides_internal_identity_fields( + monkeypatch: pytest.MonkeyPatch, capsys: Any +) -> None: + monkeypatch.setattr(render.sys.stdout, "isatty", lambda: True) + monkeypatch.setattr( + http, + "request", + lambda *_a, **_k: FakeResponse( + payload={ + "id": "review-id", + "organization_id": "org-id", + "user_id": "user-id", + "repository_full_name": "usestrix/strix", + "pr_number": 1177, + "pr_title": "Improve cloud CLI", + "verdict": "pass", + "findings": [{"severity": "high", "title": "Example"}], + } + ), + ) + + assert cloud.run_cloud(["pr-reviews", "get", "review-id"]) == 0 + output = capsys.readouterr().out + for value in ("usestrix/strix", "1177", "Improve cloud CLI", "pass", "Example"): + assert value in output + assert "org-id" not in output + assert "user-id" not in output + assert "lossless machine-readable" in output + + def test_workspace_use_accepts_list_number( monkeypatch: pytest.MonkeyPatch, tmp_path: Path ) -> None: