How do I export chat with AI?

+1 this would be great. or perhaps another way to back them up in the cloud

1 Like

+1 We need this quality-of-life feature.

1 Like

In some projects in my case the chat is the actual source code from which the code is reconstructed. It contains versions, history, justification of choices. I want to add it in git. @sheikheddy gives a good work around, but this really needs to be a central feature. I want to be able to browse all the snippets that modified a given file, I want to be able to do the equivalent of git blame on my code to get back to where a regression happened, etc.

2 Likes

I made a small script just for my specific use, hopefully will get it to be more featured in the future. It extracts the latest AI chat and prints it as a markdown:

3 Likes

this is awesome, worked great, thank you for postingā€¦

1 Like

we have the same needs

I have created a Cursor (VSCode) extension version that extracts the chats and lets you save the chats as markdown to a file or a new text using the command palette.

Let me know if youā€™re interested Iā€™m finalizing it tomorrow and making it downloadable.

5 Likes

Iā€™m in. Please share download. You could sell this one dude. :wink:

Can you make a Cursor Marketplace extension and put it in there? :slight_smile:

Iā€™ve made a cursor chat browser for both chat and composer messages (using cursor).
Feel free to check it out here. It has already helped me a lot. You can download the logs as md, pdf or html.

Feel free to contribute to improvements.

3 Likes

:fire: :fire: :fire:

1 Like

Ainā€™t it crazy that rather than fix my npm install, I found it faster to just generate the same thing with flask?

Here you go, this one finds all your conversations, orders them by time and allows you to search their content.

Done in 20 minutes with our favorite tool before a meeting.

I just asked cursor to change this script to let us specify the CHAT TITLE as an argument to find the conversation we want to print, because I thought that would be more flexible. Also, on Mac the path seems to be different for stored conversations, so I changed that too.

"""
This script extracts chat data from the most recent Cursor SQLite database and reconstructs conversations.

Functionality:
- Connects to the most recent Cursor workspace SQLite database
- Extracts 'workbench.panel.aichat.view.aichat.chatdata' from ItemTable
- Reconstructs conversations from the 'bubbles' in each tab
- Outputs conversations in Markdown format

Requirements:
- Python 3.x
- sqlite3 module (usually comes pre-installed with Python)
- os and glob modules for file operations
"""

import sqlite3
import os
import glob
import json
from datetime import datetime
import sys


def get_latest_vscdb_file():
    print(f"Debug - OS name: {os.name}, Platform: {sys.platform}")  # Add this debug line

    # Get the appropriate base path depending on the OS
    if os.name == 'nt':  # Windows
        base_path = os.path.join(os.environ['APPDATA'], 'Cursor', 'User', 'workspaceStorage')
    elif os.name == 'posix':  # macOS and Linux
        if sys.platform == 'darwin':  # macOS
            base_path = os.path.expanduser('~/Library/Application Support/Cursor/User/workspaceStorage')
        else:  # Linux
            base_path = os.path.expanduser('~/.config/Cursor/User/workspaceStorage')
    else:
        raise OSError("Unsupported operating system")

    print(f"Looking for Cursor data in: {base_path}")

    if not os.path.exists(base_path):
        raise FileNotFoundError(f"Cursor workspace storage not found at: {base_path}")

    folders = glob.glob(os.path.join(base_path, "*"))
    if not folders:
        raise FileNotFoundError("No folders found in Cursor workspace storage.")

    latest_folder = max(folders, key=os.path.getctime)
    vscdb_file = os.path.join(latest_folder, "state.vscdb")

    if not os.path.exists(vscdb_file):
        raise FileNotFoundError(f"state.vscdb not found in the latest folder: {latest_folder}")

    print(f"Latest folder: {latest_folder}")
    return vscdb_file


def extract_data_from_vscdb(db_path):
    conn = sqlite3.connect(db_path)
    cursor = conn.cursor()

    cursor.execute(
        "SELECT value FROM ItemTable WHERE key = 'workbench.panel.aichat.view.aichat.chatdata'"
    )
    result = cursor.fetchone()
    conn.close()

    if result:
        return json.loads(result[0])
    else:
        return None


def get_search_title():
    while True:
        search_title = input("Enter the title to search for in conversations: ").strip()
        if not search_title:
            print("Search title cannot be empty. Please enter a title to search for.")
            continue

        confirm = input(f"Search for conversations containing '{search_title}'? (y/n): ").lower()
        if confirm == 'y':
            return search_title
        elif confirm == 'n':
            continue
        else:
            print("Please answer 'y' or 'n'")


def reconstruct_conversations(chat_data, search_title):
    conversations = []

    for tab in chat_data.get("tabs", []):
        # Only include conversations that match the search title
        if search_title.lower() not in tab.get("chatTitle", "").lower():
            continue

        conversation = {
            "title": tab.get("chatTitle", "Untitled Chat"),
            "messages": [],
            "timestamp": tab.get("lastSendTime", 0),
        }

        for bubble in tab.get("bubbles", []):
            speaker = "Human" if bubble.get("type") == "user" else "AI"
            message = bubble.get("text", "")
            conversation["messages"].append((speaker, message))

        conversations.append(conversation)

    # Sort conversations by timestamp
    conversations.sort(key=lambda x: x["timestamp"], reverse=True)

    if not conversations:
        print(f"No conversations found with '{search_title}' in the title.")

    return conversations


def format_markdown(conversations):
    markdown = "# Cursor Chat Conversations\n\n"

    for idx, conv in enumerate(conversations, 1):
        markdown += f"## Conversation {idx}: {conv['title']}\n\n"
        markdown += f"*Timestamp: {datetime.fromtimestamp(conv['timestamp']/1000).strftime('%Y-%m-%d %H:%M:%S')}*\n\n"

        for speaker, message in conv["messages"]:
            markdown += f"### {speaker}:\n\n{message}\n\n"

        markdown += "---\n\n"

    return markdown


def main():
    try:
        db_path = get_latest_vscdb_file()
        print(f"Using database: {db_path}")

        chat_data = extract_data_from_vscdb(db_path)
        if chat_data is None:
            print("No chat data found in the database.")
            return

        # Get search title from user (now required)
        search_title = get_search_title()

        # Get filtered conversations
        conversations = reconstruct_conversations(chat_data, search_title)
        if not conversations:
            return

        markdown_output = format_markdown(conversations)

        # Print to console
        print(markdown_output)

        # Create filename using the search term
        output_file = f"{search_title.lower().replace(' ', '_')}_conversations.md"
        with open(output_file, "w", encoding="utf-8") as f:
            f.write(markdown_output)
        print(f"Markdown output saved to {output_file}")

    except Exception as e:
        print(f"An error occurred: {str(e)}")


if __name__ == "__main__":
    main()

1 Like

you should post this in Showcase if you havenā€™t already. great work!

2 Likes

who else has tested this?

Over a year ago. Any updates?

great work thomas-pedersen!!!
Just tried it, love being able to search, and simple display.
Getting a runtime error trying to display some workspaces:

anyone know why?

We built an extension that natively integrates with Cursorā€™s fork of VS Code to do this from the command palette. You can try it out here: SpecStory (Cursor Extension) - Visual Studio Marketplace

Read a bit more about it here in showcase: Built a Cursor Extension to save and share chat and composer history

2 Likes

looks like Cursor changed location for saved chats after update.

I write a small script to export all chats to markdown files:

1 Like

Coding alongside #ai can be a real #productivity booster. My current tool is @anon-10470271. But when you canā€™t properly copy that useful conversation output, itā€™s hard to save stuff for reference etc.

Thatā€™s why I created the Cursor Convo Export extension:

Use it with the command palette or via status shortcut