Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions agentrace/cli.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""agentrace CLI.

agentrace list what did my subagents do
agentrace check flag the suspicious results
agentrace show <id> read one run in full
agentrace stats where the time went
agentrace list what did my subagents do
agentrace check flag the suspicious results
agentrace show <id> read one run in full
agentrace stats where the time went
"""

from __future__ import annotations
Expand All @@ -12,6 +12,7 @@
import json
import sys
from pathlib import Path
from statistics import median

from rich.console import Console
from rich.markup import escape
Expand Down Expand Up @@ -80,7 +81,9 @@ def cmd_check(args) -> int:
continue
flagged += 1
total_findings += len(findings)
console.print(f"\n[bold]{r.description or '(no description)'}[/] [dim]{r.tool_use_id[-8:]}[/]")
console.print(
f"\n[bold]{r.description or '(no description)'}[/] [dim]{r.tool_use_id[-8:]}[/]"
)
for f in findings:
colour = _SEV_COLOUR.get(f.severity, "white")
console.print(f" [{colour}]{f.severity:<6}[/] [bold]{f.check}[/] {f.message}")
Expand Down Expand Up @@ -136,9 +139,9 @@ def cmd_stats(args) -> int:
table.add_row("errored", f"{errors:,}")
table.add_row("empty results", f"{empty:,}")
if durations:
table.add_row("total agent time", f"{total_s/3600:.1f} h")
table.add_row("median run", f"{sorted(durations)[len(durations)//2]:.0f} s")
table.add_row("slowest run", f"{max(durations)/60:.1f} min")
table.add_row("total agent time", f"{total_s / 3600:.1f} h")
table.add_row("median run", f"{median(durations):.0f} s")
table.add_row("slowest run", f"{max(durations) / 60:.1f} min")
table.add_row("prompt chars written", f"{prompt_chars:,}")
table.add_row("result chars returned", f"{result_chars:,}")
console.print(table)
Expand Down
43 changes: 43 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

import subprocess
import sys
from argparse import Namespace
from datetime import UTC, datetime, timedelta

import pytest

from agentrace import cli
from agentrace.parse import AgentRun


def run_cli(*args: str) -> subprocess.CompletedProcess[str]:
Expand All @@ -13,6 +20,42 @@ def run_cli(*args: str) -> subprocess.CompletedProcess[str]:
)


@pytest.mark.parametrize(
"durations,expected",
[
([10, 20], "15 s"),
([30, 10, 20], "20 s"),
([10], "10 s"),
([10, None, 20], "15 s"),
([None], None),
],
)
def test_stats_median(durations, expected, monkeypatch, capsys):
start = datetime(2026, 1, 1, tzinfo=UTC)
runs = [
AgentRun(
tool_use_id=str(i),
description="test",
prompt="prompt",
result="result",
started_at=start,
ended_at=start + timedelta(seconds=duration) if duration is not None else None,
)
for i, duration in enumerate(durations)
]
monkeypatch.setattr(cli, "_load", lambda args: runs)

assert cli.cmd_stats(Namespace(json=False)) == 0

rows = capsys.readouterr().out.splitlines()
median_rows = [row for row in rows if "median run" in row]
if expected is None:
assert median_rows == []
else:
assert len(median_rows) == 1
assert " ".join(median_rows[0].split()) == f"median run {expected}"


def test_file_and_dir_are_mutually_exclusive(tmp_path):
transcript = tmp_path / "session.jsonl"
transcript.write_text("")
Expand Down
Loading