Skip to content

Instantly share code, notes, and snippets.

@lushkovsky-s
Last active May 10, 2025 19:52
Show Gist options
  • Save lushkovsky-s/a336000fb9fcf934af4c852893617321 to your computer and use it in GitHub Desktop.
Save lushkovsky-s/a336000fb9fcf934af4c852893617321 to your computer and use it in GitHub Desktop.
Dump Cursor notepads as JSON (Mac version)
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} '
# 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
#!/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"
@devidw
Copy link

devidw commented Apr 15, 2025

  • get_cursor_workspace_id.sh works fine for me
  • dump_cursor_notepads.sh throws
Error: in prepare, no such table: ItemTable

Version: 0.48.9
VSCode Version: 1.96.2
Commit: 61e99179e4080fecf9d8b92c6e2e3e00fbfb53f0
Date: 2025-04-12T18:33:49.349Z (2 days ago)
Electron: 34.3.4
Chromium: 132.0.6834.210
Node.js: 20.18.3
V8: 13.2.152.41-electron.0
OS: Darwin arm64 23.6.0

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.

@lushkovsky-s
Copy link
Author

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)?

@devidw
Copy link

devidw commented May 10, 2025

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