#!/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())