diff --git a/README.md b/README.md index 183e8f1..ec6f3c0 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,10 @@ agentrace stats --json # aggregate, machine-readable Point it somewhere else with `--dir` or at one file with `--file`. +`show --max N` limits each prompt and result to at most `N` characters (default +4000). `N` must be a non-negative integer; zero hides the text, while negative +values are usage errors (exit code 2). + ## Install **PyPI:** https://pypi.org/project/agentrace-cli/ diff --git a/agentrace/cli.py b/agentrace/cli.py index eedb373..476759c 100644 --- a/agentrace/cli.py +++ b/agentrace/cli.py @@ -176,6 +176,13 @@ def cmd_stats(args) -> int: return 0 +def _nonnegative_int(value: str) -> int: + number = int(value) + if number < 0: + raise argparse.ArgumentTypeError("must be a non-negative integer") + return number + + def _existing_file(value: str) -> str: if not Path(value).is_file(): raise argparse.ArgumentTypeError(f"not a file: {value}") @@ -199,7 +206,12 @@ def main(argv: list[str] | None = None) -> int: s = sub.add_parser("show", help="read one run in full") s.add_argument("id", help="tool_use_id or its last 8 chars") - s.add_argument("--max", type=int, default=4000, help="truncate long text") + s.add_argument( + "--max", + type=_nonnegative_int, + default=4000, + help="maximum characters per prompt/result (non-negative; default: 4000)", + ) s.set_defaults(func=cmd_show) st = sub.add_parser("stats", help="aggregate stats") diff --git a/tests/test_show_limit.py b/tests/test_show_limit.py new file mode 100644 index 0000000..6f0898c --- /dev/null +++ b/tests/test_show_limit.py @@ -0,0 +1,34 @@ +import pytest + +from agentrace import cli + + +@pytest.mark.parametrize("value", ["-1", "-5"]) +def test_negative_limit_rejected_before_loading(value, monkeypatch, capsys): + def unexpected_load(args): + pytest.fail("invalid --max must be rejected before loading transcripts") + + monkeypatch.setattr(cli, "_load", unexpected_load) + with pytest.raises(SystemExit) as exc: + cli.main(["show", "abc", "--max", value]) + assert exc.value.code == 2 + captured = capsys.readouterr() + assert "--max" in captured.err + assert "non-negative" in captured.err + assert captured.out == "" + + +@pytest.mark.parametrize( + "options, expected", + [([], 4000), (["--max", "0"], 0), (["--max", "1"], 1), (["--max", "100"], 100)], +) +def test_nonnegative_limit_and_default(options, expected, monkeypatch): + seen = [] + + def show(args): + seen.append(args.max) + return 0 + + monkeypatch.setattr(cli, "cmd_show", show) + assert cli.main(["show", "abc", *options]) == 0 + assert seen == [expected]