From 6da9e9afcb3bad35e0ea904ae49896be43841b6e Mon Sep 17 00:00:00 2001 From: Toby Backstrom Date: Tue, 30 Dec 2025 07:45:01 +0100 Subject: [PATCH] Support custom-title entries in session summary Sessions renamed via /rename now display the custom title in the session picker instead of the auto-generated summary. --- src/claude_code_transcripts/__init__.py | 20 ++++++++++++++++++-- tests/test_generate_html.py | 20 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/claude_code_transcripts/__init__.py b/src/claude_code_transcripts/__init__.py index 6318120..07344cd 100644 --- a/src/claude_code_transcripts/__init__.py +++ b/src/claude_code_transcripts/__init__.py @@ -115,6 +115,23 @@ def get_session_summary(filepath, max_length=200): def _get_jsonl_summary(filepath, max_length=200): """Extract summary from JSONL file.""" try: + # First priority: custom title (user renamed session via /rename) + with open(filepath, "r", encoding="utf-8") as f: + for line in f: + line = line.strip() + if not line: + continue + try: + obj = json.loads(line) + if obj.get("type") == "custom-title" and obj.get("customTitle"): + title = obj["customTitle"] + if len(title) > max_length: + return title[: max_length - 3] + "..." + return title + except json.JSONDecodeError: + continue + + # Second priority: summary type entries with open(filepath, "r", encoding="utf-8") as f: for line in f: line = line.strip() @@ -122,7 +139,6 @@ def _get_jsonl_summary(filepath, max_length=200): continue try: obj = json.loads(line) - # First priority: summary type entries if obj.get("type") == "summary" and obj.get("summary"): summary = obj["summary"] if len(summary) > max_length: @@ -131,7 +147,7 @@ def _get_jsonl_summary(filepath, max_length=200): except json.JSONDecodeError: continue - # Second pass: find first non-meta user message + # Third pass: find first non-meta user message with open(filepath, "r", encoding="utf-8") as f: for line in f: line = line.strip() diff --git a/tests/test_generate_html.py b/tests/test_generate_html.py index b79542b..98fe8e9 100644 --- a/tests/test_generate_html.py +++ b/tests/test_generate_html.py @@ -1076,6 +1076,26 @@ def test_truncates_long_summaries(self, tmp_path): assert len(summary) <= 100 assert summary.endswith("...") + def test_gets_custom_title_from_jsonl(self, tmp_path): + """Test extracting custom title from JSONL file (set via /rename).""" + jsonl_file = tmp_path / "test.jsonl" + jsonl_file.write_text( + '{"type":"custom-title","customTitle":"GIT stuff","sessionId":"abc123"}\n' + '{"type":"user","message":{"content":"Hello"}}\n' + ) + summary = get_session_summary(jsonl_file) + assert summary == "GIT stuff" + + def test_custom_title_takes_priority_over_summary(self, tmp_path): + """Test that custom title takes priority over summary entry.""" + jsonl_file = tmp_path / "test.jsonl" + jsonl_file.write_text( + '{"type":"custom-title","customTitle":"My Custom Title","sessionId":"abc123"}\n' + '{"type":"summary","summary":"Auto-generated summary"}\n' + ) + summary = get_session_summary(jsonl_file) + assert summary == "My Custom Title" + class TestFindLocalSessions: """Tests for find_local_sessions which discovers local JSONL files."""