Update CHANGELOG.md, fix logo centering, and enhance totally reset cursor functionality. Revert cursor reset to beta, add new confirmation and display functions, and improve file handling for cursor-related paths.

This commit is contained in:
yeongpin 2025-03-28 23:27:00 +08:00
parent 1b6ba5eab8
commit 28cd662e83
4 changed files with 471 additions and 155 deletions

View File

@ -4,7 +4,10 @@
1. Add: Cursor Account Info | 增加 Cursor 賬號信息 1. Add: Cursor Account Info | 增加 Cursor 賬號信息
2. Fix: Disable Auto Update | 修復禁用自動更新 2. Fix: Disable Auto Update | 修復禁用自動更新
3. Add: 0.48.x Version Support | 增加 0.48.x 版本支持 3. Add: 0.48.x Version Support | 增加 0.48.x 版本支持
4. Fix: Some Issues | 修復一些問題 4. Revert: Totally Reser Cursor to Beta | 恢復完全重置 Cursor 到 Beta
5. Reopen: Totally Reset Cursor | 重新開啟完全重置 Cursor
6. Fix: Logo.py Center | 修復 Logo.py 居中
7. Fix: Some Issues | 修復一些問題
## v1.7.18 ## v1.7.18
1. Fix: No Write Permission | 修復沒有寫入權限 1. Fix: No Write Permission | 修復沒有寫入權限

View File

@ -20,7 +20,7 @@ init()
# get terminal width # get terminal width
def get_terminal_width(): def get_terminal_width():
try: try:
columns, _ = shutil.get_terminal_size() columns, _ = shutil.get_terminal_size()/2
return columns return columns
except: except:
return 80 # default width return 80 # default width
@ -34,7 +34,7 @@ def center_multiline_text(text, handle_chinese=False):
for line in lines: for line in lines:
# calculate actual display width (remove ANSI color codes) # calculate actual display width (remove ANSI color codes)
clean_line = line clean_line = line
for color in [Fore.CYAN, Fore.YELLOW, Fore.GREEN, Fore.RED, Style.RESET_ALL]: for color in [Fore.CYAN, Fore.YELLOW, Fore.GREEN, Fore.RED, Fore.BLUE, Style.RESET_ALL]:
clean_line = clean_line.replace(color, '') clean_line = clean_line.replace(color, '')
# remove all ANSI escape sequences to get the actual length # remove all ANSI escape sequences to get the actual length

View File

@ -504,8 +504,8 @@ def main():
print_menu() print_menu()
elif choice == "10": elif choice == "10":
import totally_reset_cursor import totally_reset_cursor
# totally_reset_cursor.main(translator) totally_reset_cursor.run(translator)
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}") # print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
print_menu() print_menu()
else: else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")

View File

@ -2,195 +2,508 @@ import os
import shutil import shutil
import platform import platform
import time import time
import sys
import glob
import json
import uuid import uuid
import random
import string
import re
from datetime import datetime
import subprocess import subprocess
from colorama import Fore, Style, init from colorama import Fore, Style, init
from main import translator
from main import EMOJI
# Initialize colorama
init() init()
# Define emoji and color constants
EMOJI = { EMOJI = {
"FILE": "📄",
"BACKUP": "💾",
"SUCCESS": "", "SUCCESS": "",
"ERROR": "", "ERROR": "",
"INFO": "", "INFO": "",
"RESET": "🔄", "RESET": "🔄",
"MENU": "📋", "MENU": "📋",
"ARROW": "",
"LANG": "🌐",
"UPDATE": "🔄",
"ADMIN": "🔐",
"STOP": "🛑",
"DISCLAIMER": "⚠️",
"WARNING": "⚠️" "WARNING": "⚠️"
} }
def delete_directory(path, translator=None): def display_banner():
"""Deletes a directory and all its contents.""" """Displays a stylized banner for the tool."""
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['STOP']} {translator.get('totally_reset.title')} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
def display_features():
"""Displays the features of the Cursor AI Reset Tool."""
print(f"\n{Fore.CYAN}{EMOJI['MENU']} {translator.get('totally_reset.feature_title')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_1')} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_2')} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_3')} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_4')} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_5')} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_6')} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_7')} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_8')} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.feature_9')} {Style.RESET_ALL}\n")
def display_disclaimer():
"""Displays a disclaimer for the user."""
print(f"\n{Fore.RED}{EMOJI['DISCLAIMER']} {translator.get('totally_reset.disclaimer_title')}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.disclaimer_1')} {Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.disclaimer_2')} {Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.disclaimer_3')} {Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.disclaimer_4')} {Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.disclaimer_5')} {Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.disclaimer_6')} {Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.disclaimer_7')} {Style.RESET_ALL} \n")
def get_confirmation():
"""Gets confirmation from the user to proceed."""
while True:
choice = input(f"{Fore.RED}{EMOJI['WARNING']} {translator.get('totally_reset.confirm_title')} (Y/n): ").strip().lower()
if choice == "y" or choice == "":
return True
elif choice == "n":
return False
else:
print(f"{EMOJI['ERROR']} {translator.get('totally_reset.invalid_choice')}")
def remove_dir(path):
"""Removes a directory if it exists and logs the action."""
# Safety check to ensure we're only deleting Cursor-related directories
if not is_cursor_related(path):
print(f"{Fore.RED}{EMOJI['WARNING']} {translator.get('totally_reset.skipped_for_safety', path=path)} {Style.RESET_ALL}")
return
if os.path.exists(path): if os.path.exists(path):
try: try:
shutil.rmtree(path) shutil.rmtree(path, ignore_errors=True)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.removed', path=path)}{Style.RESET_ALL}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.deleted', path=path)} {Style.RESET_ALL}")
except Exception as e: except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.failed_to_remove', path=path, error=e)}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.error_deleting', path=path, error=str(e))} {Style.RESET_ALL}")
else: else:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('totally_reset.not_found', path=path)}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.not_found', path=path)} {Style.RESET_ALL}")
def remove_file(path):
"""Removes a file if it exists and logs the action."""
# Safety check to ensure we're only deleting Cursor-related files
if not is_cursor_related(path):
print(f"{Fore.RED}{EMOJI['WARNING']} {translator.get('totally_reset.skipped_for_safety', path=path)} {Style.RESET_ALL}")
return
def delete_file(path, translator=None):
"""Deletes a file if it exists."""
if os.path.isfile(path): if os.path.isfile(path):
try: try:
os.remove(path) os.remove(path)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.removed', path=path)}{Style.RESET_ALL}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.deleted', path=path)} {Style.RESET_ALL}")
except Exception as e: except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.failed_to_remove', path=path, error=e)}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.error_deleting', path=path, error=str(e))} {Style.RESET_ALL}")
else: else:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('totally_reset.not_found', path=path)}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.not_found', path=path)} {Style.RESET_ALL}")
def reset_machine_id(translator=None): def is_cursor_related(path):
"""Resets the machine ID to a new UUID.""" """
new_id = str(uuid.uuid4()) Safety function to verify a path is related to Cursor before deletion.
if platform.system() == "Windows": Returns True if the path appears to be related to Cursor AI.
"""
# Skip .vscode check as it's shared with VS Code
if path.endswith(".vscode"):
return False
# Check if path contains cursor-related terms
cursor_terms = ["cursor", "cursorai", "cursor-electron"]
# Convert path to lowercase for case-insensitive matching
lower_path = path.lower()
# Return True if any cursor term is present in the path
for term in cursor_terms:
if term in lower_path:
return True
# Check specific known Cursor file patterns
cursor_patterns = [
r"\.cursor_.*$",
r"cursor-.*\.json$",
r"cursor_.*\.json$",
r"cursor-machine-id$",
r"trial_info\.json$",
r"license\.json$"
]
for pattern in cursor_patterns:
if re.search(pattern, lower_path):
return True
# If it's a specific file that we know is only for Cursor
if os.path.basename(lower_path) in [
"cursor_trial_data",
"cursor-state.json",
"cursor-machine-id",
"ai-settings.json",
"cursor.desktop"
]:
return True
return False
def find_cursor_license_files(base_path, pattern):
"""Finds files matching a pattern that might contain license information."""
try: try:
subprocess.run( matches = []
["reg", "add", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid", "/d", new_id, "/f"], for root, dirnames, filenames in os.walk(base_path):
check=True, for filename in filenames:
stdout=subprocess.PIPE, # Check if filename matches any pattern before adding to matches
stderr=subprocess.PIPE if any(p.lower() in filename.lower() for p in pattern):
) full_path = os.path.join(root, filename)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.machine_guid_reset', new_id=new_id)}{Style.RESET_ALL}") # Extra safety check to ensure it's cursor-related
except subprocess.CalledProcessError as e: if is_cursor_related(full_path):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.failed_to_reset_machine_guid', error=e)}{Style.RESET_ALL}") matches.append(full_path)
elif platform.system() == "Linux": return matches
machine_id_paths = ["/etc/machine-id", "/var/lib/dbus/machine-id"] except Exception as e:
for path in machine_id_paths: print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.error_searching', path=base_path, error=str(e))} {Style.RESET_ALL}")
if os.path.exists(path): return []
def generate_new_machine_id():
"""Generates a new random machine ID."""
return str(uuid.uuid4())
def create_fake_machine_id(path):
"""Creates a new machine ID file with random ID."""
if not is_cursor_related(path):
return
try: try:
new_id = generate_new_machine_id()
directory = os.path.dirname(path)
# Ensure directory exists
if not os.path.exists(directory):
os.makedirs(directory)
with open(path, 'w') as f: with open(path, 'w') as f:
f.write(new_id) f.write(new_id)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.machine_id_reset', path=path)}{Style.RESET_ALL}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.created_machine_id', path=path)} {Style.RESET_ALL}")
except Exception as e: except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.failed_to_reset_machine_id', path=path, error=e)}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.error_creating_machine_id', path=path, error=str(e))} {Style.RESET_ALL}")
elif platform.system() == "Darwin": # macOS
print(" macOS does not use a machine-id file. Skipping machine ID reset.") def reset_machine_id(system, home):
"""Resets machine ID in all possible locations."""
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.resetting_machine_id')} {Style.RESET_ALL}")
# Common machine ID locations based on OS
if system == "Windows":
machine_id_paths = [
os.path.join(home, "AppData", "Roaming", "Cursor", "cursor-machine-id"),
os.path.join(home, "AppData", "Local", "Cursor", "cursor-machine-id"),
os.path.join(home, "AppData", "Roaming", "cursor-electron", "cursor-machine-id"),
os.path.join(home, "AppData", "Local", "cursor-electron", "cursor-machine-id"),
os.path.join(home, ".cursor-machine-id"),
]
elif system == "Darwin": # macOS
machine_id_paths = [
os.path.join(home, "Library", "Application Support", "Cursor", "cursor-machine-id"),
os.path.join(home, "Library", "Application Support", "cursor-electron", "cursor-machine-id"),
os.path.join(home, ".cursor-machine-id"),
]
elif system == "Linux":
machine_id_paths = [
os.path.join(home, ".config", "Cursor", "cursor-machine-id"),
os.path.join(home, ".config", "cursor-electron", "cursor-machine-id"),
os.path.join(home, ".cursor-machine-id"),
]
# First remove existing machine IDs
for path in machine_id_paths:
remove_file(path)
# Then create new randomized IDs
for path in machine_id_paths:
create_fake_machine_id(path)
# Try to reset system machine ID if possible (with appropriate permissions)
if system == "Windows":
try:
# Windows: Create a temporary VBS script to reset machine GUID
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.note_complete_machine_id_reset_may_require_running_as_administrator')} {Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.windows_machine_id_modification_skipped', error=str(e))} {Style.RESET_ALL}")
elif system == "Linux":
try:
# Linux: Create a random machine-id in /etc/ (needs sudo)
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.note_complete_system_machine_id_reset_may_require_sudo_privileges')} {Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.linux_machine_id_modification_skipped', error=str(e))} {Style.RESET_ALL}")
def create_fake_trial_info(path, system, home):
"""Creates fake trial information to extend trial period."""
if not is_cursor_related(path):
return
try:
# Generate future expiry date (90 days from now)
future_date = (datetime.now().timestamp() + (90 * 24 * 60 * 60)) * 1000 # milliseconds
# Create fake trial info
fake_trial = {
"trialStartTimestamp": datetime.now().timestamp() * 1000,
"trialEndTimestamp": future_date,
"hasUsedTrial": False,
"machineId": generate_new_machine_id()
}
directory = os.path.dirname(path)
# Ensure directory exists
if not os.path.exists(directory):
os.makedirs(directory)
with open(path, 'w') as f:
json.dump(fake_trial, f)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.created_extended_trial_info', path=path)} {Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.error_creating_trial_info', path=path, error=str(e))} {Style.RESET_ALL}")
def reset_cursor():
"""Completely resets Cursor AI by removing all settings, caches, and extensions."""
system = platform.system()
home = os.path.expanduser("~")
display_banner()
display_features()
display_disclaimer()
if not get_confirmation():
print(f"\n{Fore.CYAN}{EMOJI['STOP']} {translator.get('totally_reset.reset_cancelled')} {Style.RESET_ALL}")
return
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.resetting_cursor_ai_editor')} {Style.RESET_ALL}")
# Define paths based on OS
if system == "Windows":
cursor_paths = [
os.path.join(home, "AppData", "Roaming", "Cursor"),
os.path.join(home, "AppData", "Local", "Cursor"),
os.path.join(home, "AppData", "Roaming", "cursor-electron"),
os.path.join(home, "AppData", "Local", "cursor-electron"),
os.path.join(home, "AppData", "Local", "CursorAI"),
os.path.join(home, "AppData", "Roaming", "CursorAI"),
# os.path.join(home, ".vscode"), # Removed to avoid affecting VS Code
os.path.join(home, "AppData", "Local", "Temp", "Cursor"), # Temporary data
os.path.join(home, "AppData", "Local", "Temp", "cursor-updater"),
os.path.join(home, "AppData", "Local", "Programs", "cursor"),
]
# Additional locations for license/trial files on Windows
license_search_paths = [
os.path.join(home, "AppData", "Roaming"),
os.path.join(home, "AppData", "Local"),
os.path.join(home, "AppData", "LocalLow"),
]
# Registry instructions for Windows
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.windows_registry_instructions')} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.windows_registry_instructions_2')} {Style.RESET_ALL}")
elif system == "Darwin": # macOS
cursor_paths = [
os.path.join(home, "Library", "Application Support", "Cursor"),
os.path.join(home, "Library", "Application Support", "cursor-electron"),
os.path.join(home, "Library", "Caches", "Cursor"),
os.path.join(home, "Library", "Caches", "cursor-electron"),
os.path.join(home, "Library", "Preferences", "Cursor"),
os.path.join(home, "Library", "Preferences", "cursor-electron"),
os.path.join(home, "Library", "Saved Application State", "com.cursor.Cursor.savedState"),
os.path.join(home, "Library", "HTTPStorages", "com.cursor.Cursor"),
os.path.join(home, "Library", "WebKit", "com.cursor.Cursor"),
# os.path.join(home, ".vscode"), # Removed to avoid affecting VS Code
"/Applications/Cursor.app", # Main application location
]
# Additional locations for license/trial files on macOS
license_search_paths = [
os.path.join(home, "Library", "Application Support"),
os.path.join(home, "Library", "Preferences"),
os.path.join(home, "Library", "Caches"),
]
elif system == "Linux":
cursor_paths = [
os.path.join(home, ".config", "Cursor"),
os.path.join(home, ".config", "cursor-electron"),
os.path.join(home, ".cache", "Cursor"),
os.path.join(home, ".cache", "cursor-electron"),
os.path.join(home, ".local", "share", "Cursor"),
os.path.join(home, ".local", "share", "cursor-electron"),
# os.path.join(home, ".vscode"), # Removed to avoid affecting VS Code
os.path.join(home, ".local", "share", "applications", "cursor.desktop"),
os.path.join("/usr", "share", "applications", "cursor.desktop"),
os.path.join("/opt", "Cursor"),
]
# Additional locations for license/trial files on Linux
license_search_paths = [
os.path.join(home, ".config"),
os.path.join(home, ".local", "share"),
os.path.join(home, ".cache"),
]
else: else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.unsupported_os')} {Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.unsupported_os')} {Style.RESET_ALL}")
return
def display_features_and_warnings(translator=None): # Remove main Cursor directories
"""Displays features and warnings before proceeding.""" print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.removing_main_cursor_directories_and_files')} {Style.RESET_ALL}")
print(f"\n{Fore.GREEN}{EMOJI['MENU']} {translator.get('totally_reset.title')}") for path in cursor_paths:
print("=====================================") remove_dir(path)
print(f"{translator.get('totally_reset.feature_title')}")
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_1')}")
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_2')}")
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_3')}")
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_4')}")
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_5')}")
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_6')}")
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_7')}")
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_8')}")
print(f"{Fore.GREEN}{translator.get('totally_reset.feature_9')}")
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('totally_reset.warning_title')}")
print(f"{Fore.YELLOW}{translator.get('totally_reset.warning_1')}")
print(f"{Fore.YELLOW}{translator.get('totally_reset.warning_2')}")
print(f"{Fore.YELLOW}{translator.get('totally_reset.warning_3')}")
print(f"{Fore.YELLOW}{translator.get('totally_reset.warning_4')}")
print(f"{Fore.YELLOW}{translator.get('totally_reset.warning_5')}")
print(f"{Fore.YELLOW}{translator.get('totally_reset.warning_6')}")
print(f"{Fore.YELLOW}{translator.get('totally_reset.warning_7')}")
print("=====================================\n")
def get_user_confirmation(translator=None): # Reset machine identifiers (this creates new ones)
"""Prompts the user for confirmation to proceed.""" reset_machine_id(system, home)
while True:
response = input(f"{Fore.YELLOW} {translator.get('totally_reset.confirm_title')} {translator.get('totally_reset.invalid_choice')}: ").lower().strip() # Known trial/license file patterns
if response in ['yes', 'y']: file_patterns = [
return True ".cursor_trial_data",
elif response in ['no', 'n']: "trial_info.json",
return False "license.json",
"cursor-license",
"cursor_license",
"cursor-auth",
"cursor_auth",
"cursor_subscription",
"cursor-subscription",
"cursor-state",
"cursorstate",
"cursorsettings",
"cursor-settings",
"ai-settings.json",
"cursor-machine-id",
"cursor_machine_id",
"cursor-storage"
]
# Direct known trial file paths
cursor_trial_files = [
os.path.join(home, ".cursor_trial_data"),
os.path.join(home, ".cursor_license"),
os.path.join(home, ".cursor-machine-id"),
os.path.join(home, ".cursor-state.json"),
]
# OS-specific known trial/license files
if system == "Windows":
cursor_trial_files.extend([
os.path.join(home, "AppData", "Local", "Cursor", "trial_info.json"),
os.path.join(home, "AppData", "Local", "Cursor", "license.json"),
os.path.join(home, "AppData", "Roaming", "Cursor", "trial_info.json"),
os.path.join(home, "AppData", "Roaming", "Cursor", "license.json"),
os.path.join(home, "AppData", "Roaming", "Cursor", "cursor-machine-id"),
os.path.join(home, "AppData", "Local", "Cursor", "cursor-machine-id"),
os.path.join(home, "AppData", "Local", "Cursor", "ai-settings.json"),
os.path.join(home, "AppData", "Roaming", "Cursor", "ai-settings.json"),
])
elif system == "Darwin": # macOS
cursor_trial_files.extend([
os.path.join(home, "Library", "Application Support", "Cursor", "trial_info.json"),
os.path.join(home, "Library", "Application Support", "Cursor", "license.json"),
os.path.join(home, "Library", "Preferences", "Cursor", "trial_info.json"),
os.path.join(home, "Library", "Preferences", "Cursor", "license.json"),
os.path.join(home, "Library", "Application Support", "Cursor", "cursor-machine-id"),
os.path.join(home, "Library", "Application Support", "Cursor", "ai-settings.json"),
])
elif system == "Linux":
cursor_trial_files.extend([
os.path.join(home, ".config", "Cursor", "trial_info.json"),
os.path.join(home, ".config", "Cursor", "license.json"),
os.path.join(home, ".local", "share", "Cursor", "trial_info.json"),
os.path.join(home, ".local", "share", "Cursor", "license.json"),
os.path.join(home, ".config", "Cursor", "cursor-machine-id"),
os.path.join(home, ".config", "Cursor", "ai-settings.json"),
])
# Remove known trial/license files
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.removing_known')} {Style.RESET_ALL}")
for path in cursor_trial_files:
remove_file(path)
# Deep search for additional trial/license files
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.performing_deep_scan')} {Style.RESET_ALL}")
all_found_files = []
for base_path in license_search_paths:
if os.path.exists(base_path):
found_files = find_cursor_license_files(base_path, file_patterns)
all_found_files.extend(found_files)
if all_found_files:
print(f"\n🔎 {translator.get('totally_reset.found_additional_potential_license_trial_files', count=len(all_found_files))}\n")
for file_path in all_found_files:
remove_file(file_path)
else: else:
print(f"{Fore.RED}{translator.get('totally_reset.invalid_choice')}{Style.RESET_ALL}") print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.no_additional_license_trial_files_found_in_deep_scan')} {Style.RESET_ALL}")
def reset_cursor(translator=None): # Check for and remove localStorage files that might contain settings
print(f"\n{Fore.GREEN}{EMOJI['RESET']} {translator.get('totally_reset.resetting_cursor')}\n") print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.checking_for_electron_localstorage_files')} {Style.RESET_ALL}")
if system == "Windows":
local_storage_paths = glob.glob(os.path.join(home, "AppData", "Roaming", "*cursor*", "Local Storage", "leveldb", "*"))
local_storage_paths += glob.glob(os.path.join(home, "AppData", "Local", "*cursor*", "Local Storage", "leveldb", "*"))
elif system == "Darwin":
local_storage_paths = glob.glob(os.path.join(home, "Library", "Application Support", "*cursor*", "Local Storage", "leveldb", "*"))
elif system == "Linux":
local_storage_paths = glob.glob(os.path.join(home, ".config", "*cursor*", "Local Storage", "leveldb", "*"))
# Platform-specific paths for path in local_storage_paths:
paths = [] if is_cursor_related(path):
if platform.system() == "Linux": remove_file(path)
paths = [
os.path.expanduser("~/.cursor"),
os.path.expanduser("~/.local/share/cursor"),
os.path.expanduser("~/.config/cursor"),
os.path.expanduser("~/.cache/cursor"),
"/usr/local/bin/cursor",
"/opt/cursor",
"/usr/bin/cursor",
os.path.expanduser("~/.cursor/machine-id.db"),
os.path.expanduser("~/.local/share/Cursor"),
os.path.expanduser("~/.config/Cursor"),
os.path.expanduser("~/.cache/Cursor")
]
elif platform.system() == "Darwin": # macOS
paths = [
os.path.expanduser("~/Library/Application Support/Cursor"),
os.path.expanduser("~/Library/Caches/Cursor"),
"/Applications/Cursor.app",
os.path.expanduser("~/Library/Preferences/com.cursor.app.plist"),
]
elif platform.system() == "Windows":
paths = [
os.path.expanduser("~\\AppData\\Local\\Cursor"),
os.path.expanduser("~\\AppData\\Roaming\\Cursor"),
os.path.expanduser("~\\.cursor"),
os.path.expanduser("~\\.config\\Cursor"),
os.path.expanduser("~\\.cache\\Cursor"),
"C:\\Program Files\\Cursor",
"C:\\Program Files (x86)\\Cursor",
"C:\\Users\\%USERNAME%\\AppData\\Local\\Cursor",
"C:\\Users\\%USERNAME%\\AppData\\Roaming\\Cursor",
]
# Remove directories # Create new trial files with extended expiration
for path in paths: print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.creating_new_trial_information_with_extended_period')} {Style.RESET_ALL}")
delete_directory(path, translator) if system == "Windows":
create_fake_trial_info(os.path.join(home, "AppData", "Local", "Cursor", "trial_info.json"), system, home)
create_fake_trial_info(os.path.join(home, "AppData", "Roaming", "Cursor", "trial_info.json"), system, home)
elif system == "Darwin":
create_fake_trial_info(os.path.join(home, "Library", "Application Support", "Cursor", "trial_info.json"), system, home)
elif system == "Linux":
create_fake_trial_info(os.path.join(home, ".config", "Cursor", "trial_info.json"), system, home)
# Remove common files related to Cursor print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.reset_log_1')}")
files = [ print(f" {translator.get('totally_reset.reset_log_2')}")
os.path.expanduser("~/.cursor/machine-id.db"), print(f" {translator.get('totally_reset.reset_log_3')}")
os.path.expanduser("~/.local/share/cursor.db"),
os.path.expanduser("~/.config/cursor/preferences.json"),
os.path.expanduser("~/.cache/cursor.log"),
]
for file in files: print(f"\n{Fore.GREEN}{EMOJI['INFO']} {translator.get('totally_reset.reset_log_4')} {Style.RESET_ALL}")
delete_file(file, translator) print(f" {translator.get('totally_reset.reset_log_5')} {Style.RESET_ALL}")
print(f" {translator.get('totally_reset.reset_log_6')} {Style.RESET_ALL}")
print(f" {translator.get('totally_reset.reset_log_7')} {Style.RESET_ALL}")
print(f" {translator.get('totally_reset.reset_log_8')} {Style.RESET_ALL}")
# Extra cleanup (wildcard search) print(f"\n{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.reset_log_9')} {Style.RESET_ALL}")
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('totally_reset.deep_scanning')}")
base_dirs = ["/tmp", "/var/tmp", os.path.expanduser("~")] # Linux and macOS
if platform.system() == "Windows":
base_dirs = ["C:\\Temp", "C:\\Windows\\Temp", os.path.expanduser("~")] # Windows
for base in base_dirs: if __name__ == "__main__":
for root, dirs, files in os.walk(base): try:
for dir in dirs: reset_cursor()
if "cursor" in dir.lower(): except KeyboardInterrupt:
delete_directory(os.path.join(root, dir), translator) print(f"\n\n{Fore.RED}{EMOJI['STOP']} {translator.get('totally_reset.keyboard_interrupt')} {Style.RESET_ALL}")
for file in files: sys.exit(1)
if "cursor" in file.lower(): except Exception as e:
delete_file(os.path.join(root, file), translator) print(f"\n{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.unexpected_error', error=str(e))}{Style.RESET_ALL}")
print(f" {translator.get('totally_reset.report_issue')}")
sys.exit(1)
# Reset machine ID def run(translator=None):
reset_machine_id(translator) """Entry point for the totally reset cursor functionality when called from the main menu."""
try:
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.cursor_reset_completed')}") reset_cursor()
input(f"\n\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.return_to_main_menu')} {Style.RESET_ALL}")
def main(translator=None): except KeyboardInterrupt:
start_time = time.time() print(f"\n\n{Fore.RED}{EMOJI['STOP']} {translator.get('totally_reset.process_interrupted')} {Style.RESET_ALL}")
except Exception as e:
# Display features and warnings print(f"\n{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.unexpected_error', error=str(e))}{Style.RESET_ALL}")
display_features_and_warnings(translator) print(f" {translator.get('totally_reset.report_issue')}")
input(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.press_enter_to_return_to_main_menu')} {Style.RESET_ALL}")
# Get user confirmation
if get_user_confirmation(translator):
reset_cursor(translator)
end_time = time.time()
print(f"\n{Fore.GREEN}⏱️ {translator.get('totally_reset.completed_in', time=f'{end_time - start_time:.2f} seconds')}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{translator.get('totally_reset.operation_cancelled')}{Style.RESET_ALL}")
if __name__ == '__main__':
from main import translator
main(translator)