2323from .assistant_database import (
2424 add_message ,
2525 create_conversation ,
26+ get_messages ,
2627)
2728
2829# Load environment variables from .env file if present
@@ -178,6 +179,7 @@ def __init__(self, project_name: str, project_dir: Path, conversation_id: Option
178179 self .client : Optional [ClaudeSDKClient ] = None
179180 self ._client_entered : bool = False
180181 self .created_at = datetime .now ()
182+ self ._history_loaded : bool = False # Track if we've loaded history for resumed conversations
181183
182184 async def close (self ) -> None :
183185 """Clean up resources and close the Claude client."""
@@ -195,10 +197,14 @@ async def start(self) -> AsyncGenerator[dict, None]:
195197 Initialize session with the Claude client.
196198
197199 Creates a new conversation if none exists, then sends an initial greeting.
200+ For resumed conversations, skips the greeting since history is loaded from DB.
198201 Yields message chunks as they stream in.
199202 """
203+ # Track if this is a new conversation (for greeting decision)
204+ is_new_conversation = self .conversation_id is None
205+
200206 # Create a new conversation if we don't have one
201- if self . conversation_id is None :
207+ if is_new_conversation :
202208 conv = create_conversation (self .project_dir , self .project_name )
203209 self .conversation_id = conv .id
204210 yield {"type" : "conversation_created" , "conversation_id" : self .conversation_id }
@@ -260,6 +266,7 @@ async def start(self) -> AsyncGenerator[dict, None]:
260266 model = os .getenv ("ANTHROPIC_DEFAULT_OPUS_MODEL" , "claude-opus-4-5-20251101" )
261267
262268 try :
269+ logger .info ("Creating ClaudeSDKClient..." )
263270 self .client = ClaudeSDKClient (
264271 options = ClaudeAgentOptions (
265272 model = model ,
@@ -276,25 +283,35 @@ async def start(self) -> AsyncGenerator[dict, None]:
276283 env = sdk_env ,
277284 )
278285 )
286+ logger .info ("Entering Claude client context..." )
279287 await self .client .__aenter__ ()
280288 self ._client_entered = True
289+ logger .info ("Claude client ready" )
281290 except Exception as e :
282291 logger .exception ("Failed to create Claude client" )
283292 yield {"type" : "error" , "content" : f"Failed to initialize assistant: { str (e )} " }
284293 return
285294
286- # Send initial greeting
287- try :
288- greeting = f"Hello! I'm your project assistant for **{ self .project_name } **. I can help you understand the codebase, explain features, and answer questions about the project. What would you like to know?"
295+ # Send initial greeting only for NEW conversations
296+ # Resumed conversations already have history loaded from the database
297+ if is_new_conversation :
298+ # New conversations don't need history loading
299+ self ._history_loaded = True
300+ try :
301+ greeting = f"Hello! I'm your project assistant for **{ self .project_name } **. I can help you understand the codebase, explain features, and answer questions about the project. What would you like to know?"
289302
290- # Store the greeting in the database
291- add_message (self .project_dir , self .conversation_id , "assistant" , greeting )
303+ # Store the greeting in the database
304+ add_message (self .project_dir , self .conversation_id , "assistant" , greeting )
292305
293- yield {"type" : "text" , "content" : greeting }
306+ yield {"type" : "text" , "content" : greeting }
307+ yield {"type" : "response_done" }
308+ except Exception as e :
309+ logger .exception ("Failed to send greeting" )
310+ yield {"type" : "error" , "content" : f"Failed to start conversation: { str (e )} " }
311+ else :
312+ # For resumed conversations, history will be loaded on first message
313+ # _history_loaded stays False so send_message() will include history
294314 yield {"type" : "response_done" }
295- except Exception as e :
296- logger .exception ("Failed to send greeting" )
297- yield {"type" : "error" , "content" : f"Failed to start conversation: { str (e )} " }
298315
299316 async def send_message (self , user_message : str ) -> AsyncGenerator [dict , None ]:
300317 """
@@ -321,8 +338,32 @@ async def send_message(self, user_message: str) -> AsyncGenerator[dict, None]:
321338 # Store user message in database
322339 add_message (self .project_dir , self .conversation_id , "user" , user_message )
323340
341+ # For resumed conversations, include history context in first message
342+ message_to_send = user_message
343+ if not self ._history_loaded :
344+ self ._history_loaded = True
345+ history = get_messages (self .project_dir , self .conversation_id )
346+ # Exclude the message we just added (last one)
347+ history = history [:- 1 ] if history else []
348+ # Cap history to last 35 messages to prevent context overload
349+ history = history [- 35 :] if len (history ) > 35 else history
350+ if history :
351+ # Format history as context for Claude
352+ history_lines = ["[Previous conversation history for context:]" ]
353+ for msg in history :
354+ role = "User" if msg ["role" ] == "user" else "Assistant"
355+ content = msg ["content" ]
356+ # Truncate very long messages
357+ if len (content ) > 500 :
358+ content = content [:500 ] + "..."
359+ history_lines .append (f"{ role } : { content } " )
360+ history_lines .append ("[End of history. Continue the conversation:]" )
361+ history_lines .append (f"User: { user_message } " )
362+ message_to_send = "\n " .join (history_lines )
363+ logger .info (f"Loaded { len (history )} messages from conversation history" )
364+
324365 try :
325- async for chunk in self ._query_claude (user_message ):
366+ async for chunk in self ._query_claude (message_to_send ):
326367 yield chunk
327368 yield {"type" : "response_done" }
328369 except Exception as e :
0 commit comments