Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 38 additions & 18 deletions src/extract_claude_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def display_conversation(self, jsonl_path: Path, detailed: bool = False) -> None
input("\nPress Enter to continue...")

def save_as_markdown(
self, conversation: List[Dict[str, str]], session_id: str
self, conversation: List[Dict[str, str]], session_id: str, project_name: str = ""
) -> Optional[Path]:
"""Save conversation as clean markdown file."""
if not conversation:
Expand All @@ -306,7 +306,12 @@ def save_as_markdown(
date_str = datetime.now().strftime("%Y-%m-%d")
time_str = ""

filename = f"claude-conversation-{date_str}-{session_id[:8]}.md"
# Clean project name for filename (use full path)
clean_project = project_name.replace('/', '-').replace('\\', '-').replace(' ', '-') if project_name else ""
if clean_project:
filename = f"claude-conversation-{clean_project}-{date_str}-{session_id[:8]}.md"
else:
filename = f"claude-conversation-{date_str}-{session_id[:8]}.md"
output_path = self.output_dir / filename

with open(output_path, "w", encoding="utf-8") as f:
Expand Down Expand Up @@ -344,7 +349,7 @@ def save_as_markdown(
return output_path

def save_as_json(
self, conversation: List[Dict[str, str]], session_id: str
self, conversation: List[Dict[str, str]], session_id: str, project_name: str = ""
) -> Optional[Path]:
"""Save conversation as JSON file."""
if not conversation:
Expand All @@ -361,7 +366,12 @@ def save_as_json(
else:
date_str = datetime.now().strftime("%Y-%m-%d")

filename = f"claude-conversation-{date_str}-{session_id[:8]}.json"
# Clean project name for filename (use full path)
clean_project = project_name.replace('/', '-').replace('\\', '-').replace(' ', '-') if project_name else ""
if clean_project:
filename = f"claude-conversation-{clean_project}-{date_str}-{session_id[:8]}.json"
else:
filename = f"claude-conversation-{date_str}-{session_id[:8]}.json"
output_path = self.output_dir / filename

# Create JSON structure
Expand All @@ -378,7 +388,7 @@ def save_as_json(
return output_path

def save_as_html(
self, conversation: List[Dict[str, str]], session_id: str
self, conversation: List[Dict[str, str]], session_id: str, project_name: str = ""
) -> Optional[Path]:
"""Save conversation as HTML file with syntax highlighting."""
if not conversation:
Expand All @@ -398,7 +408,12 @@ def save_as_html(
date_str = datetime.now().strftime("%Y-%m-%d")
time_str = ""

filename = f"claude-conversation-{date_str}-{session_id[:8]}.html"
# Clean project name for filename (use full path)
clean_project = project_name.replace('/', '-').replace('\\', '-').replace(' ', '-') if project_name else ""
if clean_project:
filename = f"claude-conversation-{clean_project}-{date_str}-{session_id[:8]}.html"
else:
filename = f"claude-conversation-{date_str}-{session_id[:8]}.html"
output_path = self.output_dir / filename

# HTML template with modern styling
Expand Down Expand Up @@ -523,21 +538,22 @@ def save_as_html(
return output_path

def save_conversation(
self, conversation: List[Dict[str, str]], session_id: str, format: str = "markdown"
self, conversation: List[Dict[str, str]], session_id: str, format: str = "markdown", project_name: str = ""
) -> Optional[Path]:
"""Save conversation in the specified format.

Args:
conversation: The conversation data
session_id: Session identifier
format: Output format ('markdown', 'json', 'html')
project_name: Project name to include in filename and content
"""
if format == "markdown":
return self.save_as_markdown(conversation, session_id)
return self.save_as_markdown(conversation, session_id, project_name)
elif format == "json":
return self.save_as_json(conversation, session_id)
return self.save_as_json(conversation, session_id, project_name)
elif format == "html":
return self.save_as_html(conversation, session_id)
return self.save_as_html(conversation, session_id, project_name)
else:
print(f"❌ Unsupported format: {format}")
return None
Expand Down Expand Up @@ -666,11 +682,11 @@ def list_recent_sessions(self, limit: int = None) -> List[Path]:
return sessions[:limit]

def extract_multiple(
self, sessions: List[Path], indices: List[int],
self, sessions: List[Path], indices: List[int],
format: str = "markdown", detailed: bool = False
) -> Tuple[int, int]:
"""Extract multiple sessions by index.

Args:
sessions: List of session paths
indices: Indices to extract
Expand All @@ -683,9 +699,11 @@ def extract_multiple(
for idx in indices:
if 0 <= idx < len(sessions):
session_path = sessions[idx]
# Extract project name from session path
project_name = session_path.parent.name
conversation = self.extract_conversation(session_path, detailed=detailed)
if conversation:
output_path = self.save_conversation(conversation, session_path.stem, format=format)
output_path = self.save_conversation(conversation, session_path.stem, format=format, project_name=project_name)
success += 1
msg_count = len(conversation)
print(
Expand Down Expand Up @@ -888,12 +906,13 @@ def main():
conversation = extractor.extract_conversation(selected_path, detailed=args.detailed)
if conversation:
session_id = selected_path.stem
project_name = selected_path.parent.name
if args.format == "json":
output = extractor.save_as_json(conversation, session_id)
output = extractor.save_as_json(conversation, session_id, project_name)
elif args.format == "html":
output = extractor.save_as_html(conversation, session_id)
output = extractor.save_as_html(conversation, session_id, project_name)
else:
output = extractor.save_as_markdown(conversation, session_id)
output = extractor.save_as_markdown(conversation, session_id, project_name)
print(f"✅ Saved: {output.name}")
except (EOFError, KeyboardInterrupt):
print("\n👋 Cancelled")
Expand Down Expand Up @@ -1005,7 +1024,8 @@ def launch_interactive():
conversation = extractor.extract_conversation(selected_file)
if conversation:
session_id = selected_file.stem
output = extractor.save_as_markdown(conversation, session_id)
project_name = selected_file.parent.name
output = extractor.save_as_markdown(conversation, session_id, project_name)
print(f"✅ Saved: {output.name}")
except (EOFError, KeyboardInterrupt):
print("\n👋 Cancelled")
Expand Down
9 changes: 6 additions & 3 deletions src/search_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ def main():
if extract_choice == 'y':
conversation = extractor.extract_conversation(session_paths[0])
if conversation:
output = extractor.save_as_markdown(conversation, sessions[0][1])
project_name = session_paths[0].parent.name
output = extractor.save_as_markdown(conversation, sessions[0][1], project_name)
print(f"✅ Saved: {output.name}")
else:
# Multiple results, let user choose
Expand All @@ -121,7 +122,8 @@ def main():
if extract_choice == 'y':
conversation = extractor.extract_conversation(session_paths[view_num - 1])
if conversation:
output = extractor.save_as_markdown(conversation, sessions[view_num - 1][1])
project_name = session_paths[view_num - 1].parent.name
output = extractor.save_as_markdown(conversation, sessions[view_num - 1][1], project_name)
print(f"✅ Saved: {output.name}")
except (ValueError, IndexError):
print("❌ Invalid selection")
Expand All @@ -132,7 +134,8 @@ def main():
print(f"\n📤 Extracting session {i}...")
conversation = extractor.extract_conversation(session_path)
if conversation:
output = extractor.save_as_markdown(conversation, sid)
project_name = session_path.parent.name
output = extractor.save_as_markdown(conversation, sid, project_name)
print(f"✅ Saved: {output.name}")

elif choice == 'Q':
Expand Down