95 lines
3.5 KiB
Python
95 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
||
"""Cursor state.vscdb 在线瘦身(Cursor 可开着跑)。
|
||
|
||
默认 --safe:只清可再生缓存,**不删 bubbleId/agentKv**(对话 UI 不空白)。
|
||
--aggressive:含 bubbleId(须用户确认,会致旧对话显示空白;原文在 agent-transcripts jsonl)。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import sqlite3
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
DB = Path.home() / "Library/Application Support/Cursor/User/globalStorage/state.vscdb"
|
||
|
||
# 安全:仅中间态/索引缓存,不影响对话气泡
|
||
SAFE_QUERIES = [
|
||
"DELETE FROM cursorDiskKV WHERE key LIKE 'composer.content.%'",
|
||
"DELETE FROM cursorDiskKV WHERE key LIKE 'ofsContent:%'",
|
||
"DELETE FROM cursorDiskKV WHERE key LIKE 'checkpointId:%'",
|
||
"DELETE FROM cursorDiskKV WHERE key LIKE 'codeBlockPartialInlineDiffFates:%'",
|
||
"DELETE FROM cursorDiskKV WHERE key LIKE 'codeBlockDiff:%'",
|
||
"DELETE FROM cursorDiskKV WHERE key LIKE 'inlineDiff:%'",
|
||
"DELETE FROM cursorDiskKV WHERE key LIKE 'patch-graph:%'",
|
||
"DELETE FROM cursorDiskKV WHERE key LIKE 'messageRequestContext:%'",
|
||
"DELETE FROM cursorDiskKV WHERE key LIKE 'expectedContent-v1-%'",
|
||
"DELETE FROM cursorDiskKV WHERE key LIKE 'composerVirtualRowHeights:%'",
|
||
"DELETE FROM ItemTable WHERE key = 'browserAutomation.history'",
|
||
"DELETE FROM ItemTable WHERE key = 'aiCodeTrackingLines'",
|
||
]
|
||
|
||
# 激进:会清空对话 UI(bubbleId/agentKv)
|
||
AGGRESSIVE_EXTRA = [
|
||
"DELETE FROM cursorDiskKV WHERE key LIKE 'agentKv:blob:%'",
|
||
"DELETE FROM cursorDiskKV WHERE key LIKE 'bubbleId:%'",
|
||
]
|
||
|
||
|
||
def run_slim(queries: list[str], vacuum: bool) -> int:
|
||
if not DB.is_file():
|
||
print(f"❌ 未找到 {DB}")
|
||
return 1
|
||
before_mb = DB.stat().st_size // (1024 * 1024)
|
||
print(f"📊 瘦身前: {before_mb} MB")
|
||
conn = sqlite3.connect(DB, timeout=300)
|
||
cur = conn.cursor()
|
||
cur.execute("PRAGMA busy_timeout=300000")
|
||
for q in queries:
|
||
cur.execute(q)
|
||
print(f" ✅ {cur.rowcount} 行 · {q[:55]}…")
|
||
conn.commit()
|
||
print("🔧 WAL checkpoint…")
|
||
cur.execute("PRAGMA wal_checkpoint(TRUNCATE)")
|
||
print(f" ✅ checkpoint: {cur.fetchone()}")
|
||
if vacuum:
|
||
print("🔧 VACUUM…")
|
||
cur.execute("VACUUM")
|
||
conn.commit()
|
||
cur.execute("PRAGMA wal_checkpoint(TRUNCATE)")
|
||
conn.close()
|
||
after_mb = DB.stat().st_size // (1024 * 1024)
|
||
print(f"✅ 瘦身后: {after_mb} MB(释放约 {before_mb - after_mb} MB)")
|
||
return 0
|
||
|
||
|
||
def main() -> int:
|
||
parser = argparse.ArgumentParser(description="Cursor state.vscdb 在线瘦身")
|
||
parser.add_argument(
|
||
"--aggressive",
|
||
action="store_true",
|
||
help="删除 bubbleId/agentKv(对话 UI 会空白,须人工确认)",
|
||
)
|
||
parser.add_argument("--no-vacuum", action="store_true", help="跳过 VACUUM(Hook 后台用)")
|
||
args = parser.parse_args()
|
||
|
||
queries = list(SAFE_QUERIES)
|
||
if args.aggressive:
|
||
if not sys.stdin.isatty():
|
||
print("❌ --aggressive 禁止在非交互环境自动执行(防对话消失)")
|
||
return 2
|
||
print("⚠️ 激进模式将删除 bubbleId/agentKv,旧对话 UI 会空白。")
|
||
ans = input("确认继续?(y/N) ").strip().lower()
|
||
if ans not in ("y", "yes"):
|
||
print("已取消。")
|
||
return 0
|
||
queries.extend(AGGRESSIVE_EXTRA)
|
||
else:
|
||
print("🛡️ 安全模式:保留 bubbleId/agentKv(对话不消失)")
|
||
|
||
return run_slim(queries, vacuum=not args.no_vacuum)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|