Is there a way to bulk delete?
Right now I have to click the trash bin button one by one and it will take a long time to clear everything.
Hey, unfortunately, there’s no other way at the moment, but you can rename or move your project, and then all the history will disappear.
Basta você renomear a pasta do seu projeto para qualquer nome e então os chatos irá apagar sozinho depois de alguns dias o cursos exclui eles
So when I renamed it and changed the project name back within a few minutes, the chat history still existed. Only after some time did the original chat history get deleted. My question is, how long do I have to wait specifically?
Hey, you don’t need to revert to the previous name in this case. If the name of your project is important to you, you can move it to another location on your computer, or delete the entire history manually, or reinstall Cursor.
How to delete entire history manually? I know there is a delete feature on the chat window but the ide hangs up before I can open it. Thanks.
You can delete the history only in the chat window, or you can also use the method I mentioned above.
Actually this happens so much, I wrote a cript to automate deleting history…
#!/bin/bash
CURSOR_WORKSPACES=~/Library/Application\ Support/Cursor/User/workspaceStorage
# Function to display help
show_help() {
echo "Usage: $0 [parameter]"
echo "If parameter s a project name (len!=32) - will search for the workspace id"
echo "If parameter is a workspace id (len==32) - will rename state.vscdb to state.vscdb.bad"
echo "If parameter is ALL it will list all workspaces (not clean anything)"
}
# Check if no parameters are provided
if [ $# -eq 0 ]; then
show_help
exit 0
fi
# Get the parameter
param=$1
if [[ "$param" == "ALL" ]]; then
pushd "${CURSOR_WORKSPACES}" > /dev/null
grep -r folder */workspace.json | sed -E 's/^([a-f0-9]{32}).*\/([^\/]+)"$/\1 -> \2/'
popd > /dev/null
else
if [ ${#param} -eq 32 ]; then
# Rename the file
if [ -f "${CURSOR_WORKSPACES}/${param}/state.vscdb" ]; then
mv "${CURSOR_WORKSPACES}/${param}/state.vscdb" "${CURSOR_WORKSPACES}/${param}/state.vscdb.bad"
echo "Renamed state.vscdb to state.vscdb.bad in ${param}"
else
echo "Error: state.vscdb not found in ${param}"
exit 1
fi
else
# Search for the parameter
pushd "${CURSOR_WORKSPACES}" > /dev/null
grep -r "/$param\"$" */workspace.json | sed -E 's/^([a-f0-9]{32}).*\/([^\/]+)"$/\1 -> \2/'
popd > /dev/null
fi
fi```
I used this script. Worked for me, please use with extreme caution.
#!/usr/bin/env python3
"""
Clean old Cursor chat history
"""
import os
import json
import argparse
from pathlib import Path
from datetime import datetime, timedelta
import urllib.parse
import shutil
def get_cursor_workspaces_dir():
"""Get the Cursor workspaces directory based on OS"""
if os.name == 'nt': # Windows
return Path(os.environ['APPDATA']) / 'Cursor' / 'User' / 'workspaceStorage'
else: # macOS/Linux
return Path.home() / 'Library' / 'Application Support' / 'Cursor' / 'User' / 'workspaceStorage'
def get_workspace_info(workspace_dir):
"""Extract information about a workspace"""
info = {
'workspace_id': workspace_dir.name,
'project_name': 'Unknown',
'project_path': 'Unknown',
'state_file': None,
'last_modified': None,
'size_kb': 0,
'age_days': None
}
# Get project info from workspace.json
workspace_json = workspace_dir / 'workspace.json'
if workspace_json.exists():
try:
with open(workspace_json, 'r', encoding='utf-8') as f:
data = json.load(f)
if 'folder' in data:
folder = data['folder']
# Remove file:/// prefix and decode URL encoding
folder = folder.replace('file:///', '')
folder = urllib.parse.unquote(folder)
info['project_path'] = folder
info['project_name'] = os.path.basename(folder)
except Exception:
pass
# Get state.vscdb info
state_file = workspace_dir / 'state.vscdb'
if state_file.exists():
stat = state_file.stat()
info['state_file'] = state_file
info['last_modified'] = datetime.fromtimestamp(stat.st_mtime)
info['size_kb'] = round(stat.st_size / 1024, 2)
info['age_days'] = (datetime.now() - info['last_modified']).days
return info
def clean_old_history(days_old=7, dry_run=False, delete=False):
"""Clean chat history older than specified days"""
workspaces_dir = get_cursor_workspaces_dir()
if not workspaces_dir.exists():
print(f"Error: Cursor workspace directory not found at: {workspaces_dir}")
return
cutoff_date = datetime.now() - timedelta(days=days_old)
print(f"Searching for chat history older than {days_old} days (before {cutoff_date.strftime('%Y-%m-%d %H:%M:%S')})...")
print()
processed_count = 0
skipped_count = 0
# Scan all workspaces
for workspace_dir in workspaces_dir.iterdir():
if not workspace_dir.is_dir():
continue
info = get_workspace_info(workspace_dir)
if not info['state_file']:
continue
if info['last_modified'] < cutoff_date:
print(f"\033[93mFound: {info['project_name']}\033[0m")
print(f" Workspace ID: {info['workspace_id']}")
print(f" Last Modified: {info['last_modified'].strftime('%Y-%m-%d %H:%M:%S')}")
print(f" Age: {info['age_days']} days")
if dry_run:
action = 'deleted' if delete else 'renamed to state.vscdb.old'
print(f" \033[95mAction: [DRY RUN] Would be {action}\033[0m")
processed_count += 1
else:
try:
if delete:
info['state_file'].unlink()
print(f" \033[92mAction: Deleted\033[0m")
else:
backup_file = info['state_file'].with_suffix('.vscdb.old')
# If backup exists, append timestamp
if backup_file.exists():
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
backup_file = info['state_file'].with_suffix(f'.vscdb.old.{timestamp}')
shutil.move(str(info['state_file']), str(backup_file))
print(f" \033[92mAction: Renamed to {backup_file.name}\033[0m")
processed_count += 1
except Exception as e:
print(f" \033[91mAction: Failed - {e}\033[0m")
print()
else:
skipped_count += 1
# Summary
print("=" * 60)
if dry_run:
print("\033[95mDRY RUN COMPLETE\033[0m")
print(f"Would process: {processed_count} workspace(s)")
else:
print("\033[92mCOMPLETE\033[0m")
print(f"Processed: {processed_count} workspace(s)")
print(f"Skipped (newer): {skipped_count} workspace(s)")
print("=" * 60)
def main():
parser = argparse.ArgumentParser(
description='Clean old Cursor chat history',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python clean_old_chat_history.py --dry-run # Preview what would be changed
python clean_old_chat_history.py # Rename history older than 7 days
python clean_old_chat_history.py --days-old 14 # Rename history older than 14 days
python clean_old_chat_history.py --delete # Permanently delete old files
"""
)
parser.add_argument('--days-old', '-d', type=int, default=7,
help='Delete history older than this many days (default: 7)')
parser.add_argument('--dry-run', '-n', action='store_true',
help='Show what would be deleted without actually deleting')
parser.add_argument('--delete', action='store_true',
help='Permanently delete files (default: rename to .old)')
args = parser.parse_args()
clean_old_history(args.days_old, args.dry_run, args.delete)
if __name__ == '__main__':
main()