141 lines
5.0 KiB
Python
141 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
||
"""Cursor renderer code:5 崩溃修复 — 重置多窗叠加 + 降 UI 负载。不删 composerData。"""
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import datetime
|
||
import json
|
||
import shutil
|
||
import sqlite3
|
||
import subprocess
|
||
import sys
|
||
import time
|
||
from pathlib import Path
|
||
|
||
HOME = Path.home()
|
||
CUR = HOME / "Library/Application Support/Cursor"
|
||
STORAGE = CUR / "User/globalStorage/storage.json"
|
||
DB = CUR / "User/globalStorage/state.vscdb"
|
||
SETTINGS = CUR / "User/settings.json"
|
||
BACKUPS = CUR / "Backups"
|
||
|
||
|
||
def cursor_running() -> bool:
|
||
r = subprocess.run(
|
||
["pgrep", "-f", "/Applications/Cursor.app/Contents/MacOS/Cursor"],
|
||
capture_output=True,
|
||
)
|
||
return r.returncode == 0
|
||
|
||
|
||
def reset_windows() -> None:
|
||
if not STORAGE.exists():
|
||
print("⚠️ storage.json 不存在,跳过")
|
||
return
|
||
ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||
bak = STORAGE.with_suffix(f".json.bak.{ts}")
|
||
shutil.copy2(STORAGE, bak)
|
||
d = json.loads(STORAGE.read_text(encoding="utf-8"))
|
||
bw = d.setdefault("backupWorkspaces", {})
|
||
n_empty = len(bw.get("emptyWindows", []))
|
||
bw["emptyWindows"] = []
|
||
if len(bw.get("workspaces", [])) > 1:
|
||
bw["workspaces"] = bw["workspaces"][:1]
|
||
ws = d.setdefault("windowsState", {})
|
||
ws["openedWindows"] = []
|
||
ws.pop("lastActiveWindow", None)
|
||
STORAGE.write_text(json.dumps(d, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||
print(f"✅ emptyWindows {n_empty}→0 · 备份 {bak.name}")
|
||
if BACKUPS.exists():
|
||
shutil.rmtree(BACKUPS)
|
||
BACKUPS.mkdir(parents=True, exist_ok=True)
|
||
print("✅ Backups 已清空")
|
||
|
||
|
||
def patch_settings() -> None:
|
||
if not SETTINGS.exists():
|
||
return
|
||
d = json.loads(SETTINGS.read_text(encoding="utf-8"))
|
||
d.update(
|
||
{
|
||
"window.restoreWindows": "none",
|
||
"window.openWithoutArgumentsInNewWindow": "off",
|
||
"workbench.editor.restoreViewState": False,
|
||
"workbench.startupEditor": "none",
|
||
"workbench.editor.limit.enabled": True,
|
||
"workbench.editor.limit.value": 6,
|
||
"git.openRepositoryInParentFolders": "never",
|
||
"git.autorefresh": False,
|
||
"git.untrackedChanges": "hidden",
|
||
"scm.diffDecorations": "none",
|
||
"java.enabled": False,
|
||
"java.autobuild.enabled": False,
|
||
"cursor.general.enableCodebaseIndexing": False,
|
||
"files.exclude": {
|
||
**(d.get("files.exclude") or {}),
|
||
"**/.cursor/rules/persistent-chat.mdc": True,
|
||
},
|
||
}
|
||
)
|
||
SETTINGS.write_text(json.dumps(d, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||
print("✅ settings.json 已加固")
|
||
|
||
|
||
def clean_cache_db(force: bool = False) -> bool:
|
||
if not DB.exists():
|
||
return True
|
||
if cursor_running() and not force:
|
||
print("⏭ Cursor 正在运行,跳过 state.vscdb(避免 database is locked)")
|
||
print(" → Cmd+Q 退出 Cursor 后,再跑: bash cursor_code5_fix.sh --db")
|
||
return False
|
||
for attempt in range(3):
|
||
try:
|
||
con = sqlite3.connect(str(DB), timeout=2)
|
||
cur = con.cursor()
|
||
cur.execute(
|
||
"DELETE FROM ItemTable WHERE key LIKE 'workbench.panel.aichat.%' "
|
||
"OR key LIKE 'workbench.panel.composerChatViewPane.%'"
|
||
)
|
||
cur.execute("PRAGMA wal_checkpoint(TRUNCATE)")
|
||
con.commit()
|
||
cur.execute("VACUUM")
|
||
con.commit()
|
||
cur.execute("SELECT COUNT(*) FROM cursorDiskKV WHERE key LIKE 'composerData:%'")
|
||
cd = cur.fetchone()[0]
|
||
mb = DB.stat().st_size // (1024 * 1024)
|
||
con.close()
|
||
print(f"✅ state.vscdb {mb}MB · composerData={cd}(列表保留)")
|
||
return True
|
||
except sqlite3.OperationalError as e:
|
||
if "locked" in str(e).lower() and attempt < 2:
|
||
time.sleep(1)
|
||
continue
|
||
print(f"⏭ DB 被占用: {e}")
|
||
print(" → 请先 Cmd+Q 完全退出 Cursor,再跑: bash cursor_code5_fix.sh --db")
|
||
return False
|
||
return False
|
||
|
||
|
||
def main() -> None:
|
||
ap = argparse.ArgumentParser(description="Fix Cursor code:5 crashes")
|
||
ap.add_argument("--db", action="store_true", help="仅清理 state.vscdb(需 Cursor 已退出)")
|
||
ap.add_argument("--force-db", action="store_true", help="强制尝试写 DB(仍可能失败)")
|
||
args = ap.parse_args()
|
||
|
||
if args.db:
|
||
ok = clean_cache_db(force=True)
|
||
sys.exit(0 if ok else 1)
|
||
|
||
print("🛠 Cursor code:5 修复开始…")
|
||
if cursor_running():
|
||
print("ℹ️ Cursor 仍在运行 — 窗口/settings 可在线改,DB 须退出后补跑 --db")
|
||
reset_windows()
|
||
patch_settings()
|
||
clean_cache_db(force=args.force_db)
|
||
print("\n⚠️ 崩溃弹窗:勾选 Don't restore editors → 点 Close(别点 Reopen/New Window 叠窗)")
|
||
print("⚠️ persistent-chat 规则已 alwaysApply:false,不再每轮强制 MCP 挂起")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|