From 504d238af1e145e8f6218bff49ec25ac71866cb5 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Wed, 22 Apr 2026 17:08:01 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20#160=20=E2=80=94=20add=20list=5Fsessions?= =?UTF-8?q?,=20session=5Fexists,=20delete=5Fsession=20to=20session=5Fstore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - list_sessions(directory=None) -> list[str]: enumerate stored session IDs - session_exists(session_id, directory=None) -> bool: check existence without FileNotFoundError - delete_session(session_id, directory=None) -> bool: unlink a session file - load_session now raises typed SessionNotFoundError (subclass of KeyError) instead of FileNotFoundError - Claws can now manage session lifecycle without reaching past the module to glob filesystem Closes ROADMAP #160. Acceptance: claw can call list_sessions(), session_exists(id), delete_session(id) without importing Path or knowing .port_sessions/.json layout. --- src/session_store.py | 57 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/session_store.py b/src/session_store.py index 5f7502a..abc5718 100644 --- a/src/session_store.py +++ b/src/session_store.py @@ -26,10 +26,65 @@ def save_session(session: StoredSession, directory: Path | None = None) -> Path: def load_session(session_id: str, directory: Path | None = None) -> StoredSession: target_dir = directory or DEFAULT_SESSION_DIR - data = json.loads((target_dir / f'{session_id}.json').read_text()) + try: + data = json.loads((target_dir / f'{session_id}.json').read_text()) + except FileNotFoundError: + raise SessionNotFoundError(f'session {session_id!r} not found in {target_dir}') from None return StoredSession( session_id=data['session_id'], messages=tuple(data['messages']), input_tokens=data['input_tokens'], output_tokens=data['output_tokens'], ) + + +class SessionNotFoundError(KeyError): + """Raised when a session does not exist in the store.""" + pass + + +def list_sessions(directory: Path | None = None) -> list[str]: + """List all stored session IDs in the target directory. + + Args: + directory: Target session directory. Defaults to DEFAULT_SESSION_DIR. + + Returns: + Sorted list of session IDs (JSON filenames without .json extension). + """ + target_dir = directory or DEFAULT_SESSION_DIR + if not target_dir.exists(): + return [] + return sorted(p.stem for p in target_dir.glob('*.json')) + + +def session_exists(session_id: str, directory: Path | None = None) -> bool: + """Check if a session exists without raising an error. + + Args: + session_id: The session ID to check. + directory: Target session directory. Defaults to DEFAULT_SESSION_DIR. + + Returns: + True if the session file exists, False otherwise. + """ + target_dir = directory or DEFAULT_SESSION_DIR + return (target_dir / f'{session_id}.json').exists() + + +def delete_session(session_id: str, directory: Path | None = None) -> bool: + """Delete a session file from the store. + + Args: + session_id: The session ID to delete. + directory: Target session directory. Defaults to DEFAULT_SESSION_DIR. + + Returns: + True if the session was deleted, False if it did not exist. + """ + target_dir = directory or DEFAULT_SESSION_DIR + path = target_dir / f'{session_id}.json' + if path.exists(): + path.unlink() + return True + return False