diff --git a/claude_code_log/converter.py b/claude_code_log/converter.py index 86fe6702..f21e2868 100644 --- a/claude_code_log/converter.py +++ b/claude_code_log/converter.py @@ -262,6 +262,9 @@ def load_transcript( # Parse from source file messages: list[TranscriptEntry] = [] agent_ids: set[str] = set() # Collect agentId references while parsing + # Track unrecognized message types already warned about so we emit at + # most one warning per distinct type per file (these tend to repeat a lot). + warned_unrecognized_types: set[str | None] = set() try: f = open(jsonl_path, "r", encoding="utf-8", errors="replace") @@ -347,7 +350,10 @@ def load_transcript( # agent-name). Warn so we notice when Claude Code # introduces new metadata worth supporting — add to # SILENT_SKIP_TYPES once confirmed safe to drop. - if not silent: + # Only warn on the first occurrence of each distinct + # type per file to avoid flooding the output. + if not silent and entry_type not in warned_unrecognized_types: + warned_unrecognized_types.add(entry_type) print( f"Line {line_no} of {jsonl_path}: unrecognized message type " f"{entry_type!r} - skipping" diff --git a/test/test_silent_skip.py b/test/test_silent_skip.py index f2d66fe8..b892d9de 100644 --- a/test/test_silent_skip.py +++ b/test/test_silent_skip.py @@ -180,6 +180,32 @@ def test_unknown_without_uuid_warns( assert "unrecognized message type" in captured.out assert repr(entry["type"]) in captured.out + def test_repeated_unknown_type_warns_only_once( + self, tmp_path: Path, capsys: pytest.CaptureFixture[str] + ) -> None: + """The same unrecognized type repeated within a file warns once, + but each distinct type still gets its own first-occurrence warning.""" + jsonl = tmp_path / "session.jsonl" + _write_jsonl( + jsonl, + [ + {"type": "mode", "payload": 1}, + {"type": "pr-link", "url": "x"}, + {"type": "mode", "payload": 2}, + {"type": "pr-link", "url": "y"}, + {"type": "mode", "payload": 3}, + ], + ) + + messages = load_transcript(jsonl, silent=False) + captured = capsys.readouterr() + + assert messages == [] + # One warning per distinct type, not per occurrence. + assert captured.out.count("unrecognized message type") == 2 + assert captured.out.count("'mode'") == 1 + assert captured.out.count("'pr-link'") == 1 + def test_silent_mode_suppresses_warning( self, tmp_path: Path, capsys: pytest.CaptureFixture[str] ) -> None: