Last active
May 10, 2025 19:52
-
-
Save lushkovsky-s/a336000fb9fcf934af4c852893617321 to your computer and use it in GitHub Desktop.
Dump Cursor notepads as JSON (Mac version)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
WORKSPACE_ID=$(./get_cursor_workspace_id.sh <path>) sqlite3 "$HOME/Library/Application Support/Cursor/User/workspaceStorage/$WORKSPACE_ID/st | |
ate.vscdb" "SELECT value FROM ItemTable WHERE key = 'notepad.reactiveStorageId';" | python3 -c "import sys, json; pri | |
nt(json.dumps(json.loads(sys.stdin.read()), indent=2))" 2>/dev/null | jq '.notepads | to_entries[] | .value | {name: | |
.name, content: .text} ' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# get_cursor_workspace_id.sh - Get Cursor workspace ID for a given path | |
# Check if path argument is provided | |
if [ $# -ne 1 ]; then | |
echo "Usage: 0 USD /path/to/workspace" >&2 | |
exit 1 | |
fi | |
# Normalize input path (remove trailing slash) | |
SEARCH_PATH="${1%/}" | |
# Expand tilde in path | |
if [[ "$SEARCH_PATH" == "~"* ]]; then | |
SEARCH_PATH="${SEARCH_PATH/#\~/$HOME}" | |
fi | |
# Look for workspace folders in Cursor storage | |
CURSOR_STORAGE_DIR="$HOME/Library/Application Support/Cursor/User/workspaceStorage" | |
# Try alternative location if not found | |
if [ ! -d "$CURSOR_STORAGE_DIR" ]; then | |
CURSOR_STORAGE_DIR="$HOME/Library/Application Support/Cursor/workspaceStorage" | |
if [ ! -d "$CURSOR_STORAGE_DIR" ]; then | |
echo "Error: Cursor workspace storage not found" >&2 | |
exit 1 | |
fi | |
fi | |
# Find workspace ID for the specified path | |
for dir in "$CURSOR_STORAGE_DIR"/*; do | |
if [ ! -d "$dir" ]; then | |
continue | |
fi | |
dir_id=$(basename "$dir") | |
# Check if workspace.json exists and extract folder path | |
if [ -f "$dir/workspace.json" ]; then | |
folder_path=$(grep -o '"folder":[^,]*' "$dir/workspace.json" 2>/dev/null | sed 's/"folder": "file:\/\/\///g' | sed 's/"//g') | |
if [ -n "$folder_path" ]; then | |
# Add leading slash if needed | |
if [[ $folder_path == Users/* ]]; then | |
folder_path="/$folder_path" | |
fi | |
# Remove trailing slash | |
folder_path="${folder_path%/}" | |
# Check for exact path match | |
if [ "$folder_path" = "$SEARCH_PATH" ]; then | |
echo "$dir_id" | |
exit 0 | |
fi | |
fi | |
fi | |
done | |
# No workspace found | |
echo "Error: No workspace found for path: $SEARCH_PATH" >&2 | |
exit 1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Validate platform | |
[[ "$(uname)" != "Darwin" ]] && echo "❌ macOS only" && exit 1 | |
# Check dependencies | |
for bin in sqlite3 jq python3 realpath; do | |
command -v "$bin" >/dev/null || { echo "❌ Missing: $bin"; exit 1; } | |
done | |
# Usage | |
if [ $# -lt 2 ]; then | |
echo "Usage: migrate_cursor_notepads.sh /from/workspace /to/workspace [--dry-run]" >&2 | |
exit 1 | |
fi | |
FROM="$(realpath "${1%/}")" | |
TO="$(realpath "${2%/}")" | |
DRY_RUN=0 && [[ "$3" == "--dry-run" ]] && DRY_RUN=1 | |
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
# Resolve workspace IDs | |
SRC_ID=$("$SCRIPT_DIR/get_cursor_workspace_id.sh" "$FROM") || exit 1 | |
DST_ID=$("$SCRIPT_DIR/get_cursor_workspace_id.sh" "$TO") || exit 1 | |
SRC_DB="$HOME/Library/Application Support/Cursor/User/workspaceStorage/$SRC_ID/state.vscdb" | |
DST_DB="$HOME/Library/Application Support/Cursor/User/workspaceStorage/$DST_ID/state.vscdb" | |
# Backup | |
BACKUP_DIR="$HOME/.cursor_backups" | |
mkdir -p "$BACKUP_DIR" | |
TS=$(date +"%Y%m%d-%H%M%S") | |
cp "$DST_DB" "$BACKUP_DIR/state.vscdb.bak.$DST_ID.$TS" || { echo "❌ Backup failed"; exit 1; } | |
# Extract notepads | |
RAW=$(sqlite3 "$SRC_DB" "SELECT value FROM ItemTable WHERE key = 'notepad.reactiveStorageId';") | |
[[ -z "$RAW" ]] && echo "⚠️ No notepads found in source" && exit 1 | |
# Dry-run mode | |
if [ "$DRY_RUN" -eq 1 ]; then | |
echo "🧪 Dry-run only — showing source notepads:" | |
echo "$RAW" | jq '.notepads | to_entries[] | .value | {name: .name, text: (.text[0:60] + "...")}' | |
exit 0 | |
fi | |
# Serialize JSON safely for SQLite | |
ESCAPED_JSON=$(python3 -c 'import sys, json; print(json.dumps(sys.stdin.read()))' <<< "$RAW") | |
# Replace or insert key safely | |
KEY_EXISTS=$(sqlite3 "$DST_DB" "SELECT 1 FROM ItemTable WHERE key = 'notepad.reactiveStorageId';") | |
if [[ "$KEY_EXISTS" == "1" ]]; then | |
sqlite3 "$DST_DB" "UPDATE ItemTable SET value = $ESCAPED_JSON WHERE key = 'notepad.reactiveStorageId';" | |
else | |
sqlite3 "$DST_DB" "INSERT INTO ItemTable (key, value) VALUES ('notepad.reactiveStorageId', $ESCAPED_JSON);" | |
fi | |
[[ $? -eq 0 ]] && echo "✅ Migration complete. Backup: $BACKUP_DIR/state.vscdb.bak.$DST_ID.$TS" || echo "❌ DB write failed" |
Hey @devidw
Sorry to hear that, ig it's related to the latest cursor updates
Could you share the .vscdb file from your workspace ($HOME/Library/Application Support/Cursor/User/workspaceStorage/$WORKSPACE_ID/state.vscdb
)?
ah sry i didn't see this earlier, i made by deal with the lost notes, but much appreciated @lushkovsky-s <3
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Also I should note that I lost my notepads after restart, at least from the UI, was wondering if I'd be able to get them back with this script.
But maybe the reason the table doesn't exist is bc the notepads are gone forever.