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.
try: """
subprocess.run( # Skip .vscode check as it's shared with VS Code
["reg", "add", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid", "/d", new_id, "/f"], if path.endswith(".vscode"):
check=True, return False
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.machine_guid_reset', new_id=new_id)}{Style.RESET_ALL}")
except subprocess.CalledProcessError as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.failed_to_reset_machine_guid', error=e)}{Style.RESET_ALL}")
elif platform.system() == "Linux":
machine_id_paths = ["/etc/machine-id", "/var/lib/dbus/machine-id"]
for path in machine_id_paths:
if os.path.exists(path):
try:
with open(path, 'w') as f:
f.write(new_id)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.machine_id_reset', path=path)}{Style.RESET_ALL}")
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}")
elif platform.system() == "Darwin": # macOS
print(" macOS does not use a machine-id file. Skipping machine ID reset.")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.unsupported_os')}{Style.RESET_ALL}")
def display_features_and_warnings(translator=None): # Check if path contains cursor-related terms
"""Displays features and warnings before proceeding.""" cursor_terms = ["cursor", "cursorai", "cursor-electron"]
print(f"\n{Fore.GREEN}{EMOJI['MENU']} {translator.get('totally_reset.title')}")
print("=====================================")
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): # Convert path to lowercase for case-insensitive matching
"""Prompts the user for confirmation to proceed.""" lower_path = path.lower()
while True:
response = input(f"{Fore.YELLOW} {translator.get('totally_reset.confirm_title')} {translator.get('totally_reset.invalid_choice')}: ").lower().strip() # Return True if any cursor term is present in the path
if response in ['yes', 'y']: for term in cursor_terms:
if term in lower_path:
return True return True
elif response in ['no', 'n']:
return False
else:
print(f"{Fore.RED}{translator.get('totally_reset.invalid_choice')}{Style.RESET_ALL}")
def reset_cursor(translator=None): # Check specific known Cursor file patterns
print(f"\n{Fore.GREEN}{EMOJI['RESET']} {translator.get('totally_reset.resetting_cursor')}\n") cursor_patterns = [
r"\.cursor_.*$",
# Platform-specific paths r"cursor-.*\.json$",
paths = [] r"cursor_.*\.json$",
if platform.system() == "Linux": r"cursor-machine-id$",
paths = [ r"trial_info\.json$",
os.path.expanduser("~/.cursor"), r"license\.json$"
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
for path in paths:
delete_directory(path, translator)
# Remove common files related to Cursor
files = [
os.path.expanduser("~/.cursor/machine-id.db"),
os.path.expanduser("~/.local/share/cursor.db"),
os.path.expanduser("~/.config/cursor/preferences.json"),
os.path.expanduser("~/.cache/cursor.log"),
] ]
for file in files: for pattern in cursor_patterns:
delete_file(file, translator) if re.search(pattern, lower_path):
return True
# Extra cleanup (wildcard search) # If it's a specific file that we know is only for Cursor
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('totally_reset.deep_scanning')}") if os.path.basename(lower_path) in [
base_dirs = ["/tmp", "/var/tmp", os.path.expanduser("~")] # Linux and macOS "cursor_trial_data",
if platform.system() == "Windows": "cursor-state.json",
base_dirs = ["C:\\Temp", "C:\\Windows\\Temp", os.path.expanduser("~")] # Windows "cursor-machine-id",
"ai-settings.json",
"cursor.desktop"
]:
return True
for base in base_dirs: return False
for root, dirs, files in os.walk(base):
for dir in dirs:
if "cursor" in dir.lower():
delete_directory(os.path.join(root, dir), translator)
for file in files:
if "cursor" in file.lower():
delete_file(os.path.join(root, file), translator)
# Reset machine ID def find_cursor_license_files(base_path, pattern):
reset_machine_id(translator) """Finds files matching a pattern that might contain license information."""
try:
matches = []
for root, dirnames, filenames in os.walk(base_path):
for filename in filenames:
# Check if filename matches any pattern before adding to matches
if any(p.lower() in filename.lower() for p in pattern):
full_path = os.path.join(root, filename)
# Extra safety check to ensure it's cursor-related
if is_cursor_related(full_path):
matches.append(full_path)
return matches
except Exception as e:
print(f"{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.error_searching', path=base_path, error=str(e))} {Style.RESET_ALL}")
return []
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.cursor_reset_completed')}") def generate_new_machine_id():
"""Generates a new random machine ID."""
return str(uuid.uuid4())
def main(translator=None): def create_fake_machine_id(path):
start_time = time.time() """Creates a new machine ID file with random ID."""
if not is_cursor_related(path):
return
# Display features and warnings try:
display_features_and_warnings(translator) 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:
f.write(new_id)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.created_machine_id', path=path)} {Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.error_creating_machine_id', path=path, error=str(e))} {Style.RESET_ALL}")
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"),
]
# 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: else:
print(f"\n{Fore.RED}{translator.get('totally_reset.operation_cancelled')}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('totally_reset.unsupported_os')} {Style.RESET_ALL}")
return
if __name__ == '__main__': # Remove main Cursor directories
from main import translator print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.removing_main_cursor_directories_and_files')} {Style.RESET_ALL}")
main(translator) for path in cursor_paths:
remove_dir(path)
# Reset machine identifiers (this creates new ones)
reset_machine_id(system, home)
# Known trial/license file patterns
file_patterns = [
".cursor_trial_data",
"trial_info.json",
"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:
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.no_additional_license_trial_files_found_in_deep_scan')} {Style.RESET_ALL}")
# Check for and remove localStorage files that might contain settings
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", "*"))
for path in local_storage_paths:
if is_cursor_related(path):
remove_file(path)
# Create new trial files with extended expiration
print(f"\n{Fore.CYAN}{EMOJI['RESET']} {translator.get('totally_reset.creating_new_trial_information_with_extended_period')} {Style.RESET_ALL}")
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)
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('totally_reset.reset_log_1')}")
print(f" {translator.get('totally_reset.reset_log_2')}")
print(f" {translator.get('totally_reset.reset_log_3')}")
print(f"\n{Fore.GREEN}{EMOJI['INFO']} {translator.get('totally_reset.reset_log_4')} {Style.RESET_ALL}")
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}")
print(f"\n{Fore.RED}{EMOJI['INFO']} {translator.get('totally_reset.reset_log_9')} {Style.RESET_ALL}")
if __name__ == "__main__":
try:
reset_cursor()
except KeyboardInterrupt:
print(f"\n\n{Fore.RED}{EMOJI['STOP']} {translator.get('totally_reset.keyboard_interrupt')} {Style.RESET_ALL}")
sys.exit(1)
except Exception as e:
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)
def run(translator=None):
"""Entry point for the totally reset cursor functionality when called from the main menu."""
try:
reset_cursor()
input(f"\n\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.return_to_main_menu')} {Style.RESET_ALL}")
except KeyboardInterrupt:
print(f"\n\n{Fore.RED}{EMOJI['STOP']} {translator.get('totally_reset.process_interrupted')} {Style.RESET_ALL}")
except Exception as e:
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')}")
input(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('totally_reset.press_enter_to_return_to_main_menu')} {Style.RESET_ALL}")