11"""Finds where Claude Code stores its .jsonl session files on disk and
22lists projects/sessions from that directory."""
33
4+ import json
5+ import logging
46import os
57import platform
68
79from models .project import ProjectDict , SessionListItemDict
810
11+ _logger = logging .getLogger (__name__ )
12+
913
1014def safe_join (base : str , * parts : str ) -> str :
1115 """Join path components and verify the result stays under base.
@@ -55,9 +59,9 @@ def list_projects(base_dir: str | None = None) -> list[ProjectDict]:
5559 display_name = name
5660 for jf in jsonl_files :
5761 candidate = _get_display_name (
58- os .path .join (project_dir , jf ), name
62+ os .path .join (project_dir , jf ), None
5963 )
60- if candidate :
64+ if candidate is not None :
6165 display_name = candidate
6266 break
6367 projects .append ({
@@ -70,10 +74,9 @@ def list_projects(base_dir: str | None = None) -> list[ProjectDict]:
7074 return projects
7175
7276
73- def _get_display_name (jsonl_path : str , fallback : str | None ) -> str :
77+ def _get_display_name (jsonl_path : str , fallback : str | None ) -> str | None :
7478 """Peek at the first entry's cwd field to get a human-readable project path
7579 instead of the hashed directory name."""
76- import json
7780 try :
7881 with open (jsonl_path , "r" , encoding = "utf-8" , errors = "replace" ) as f :
7982 for line in f :
@@ -89,9 +92,11 @@ def _get_display_name(jsonl_path: str, fallback: str | None) -> str:
8992 folder = cwd .rsplit ("/" , 1 )[- 1 ]
9093 out = folder [:1 ].upper () + folder [1 :] if folder else cwd
9194 return str (out )
92- except Exception :
93- pass
94- return fallback or ""
95+ except (OSError , json .JSONDecodeError , UnicodeDecodeError ) as exc :
96+ _logger .warning (
97+ "Failed to extract display name from %s: %s" , jsonl_path , exc
98+ )
99+ return fallback
95100
96101
97102def list_sessions (project_dir : str ) -> list [SessionListItemDict ]:
0 commit comments