Syncing VS Code Editor to Obsidian on macOS
As I have begun digging into the world of AI-assisted editing for both code and non-code files, I have been frustrated by the inability of ChatGPT to interact directly with files that are open in Obsidian the way it can through VS Code. Since I usually have both Obsidian and VS Code open at the same time, this problem could be solved by simply making sure that Obsidian and VS Code are looking at the same file at the same time.
This was also a good exercise in accomplishing specific real-world results with the help of ChatGPT.
🧩 Components Used
The setup involved three main steps:
Obsidian Current File Plugin
We installed the Current File community plugin, which writes the path of the currently open file in Obsidian to a fixed location on disk. This gave us a simple and reliable way to monitor which file is currently active.
Shell Script Using fswatch
We wrote a lightweight shell script that uses fswatch to monitor changes to the current file output. When it detects that Obsidian has focused a new file, it:
Checks if that file exists
Switches the currently open tab in VS Code (if applicable) to match the new file
Falls back gracefully if VS Code isn’t open or the file is missing
#!/bin/bash
# Ensure your script has access to fswatch and jq
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
# Set up error logging
exec >> /tmp/watchobsidian.debug.log 2>&1
echo "watch-obsidian-selection.sh started from $0 at $(date)"
# CONFIGURATION
WATCH_FILE="$HOME/<path>/<to-obsidian-vault>/.obsidian/current-file.json"
# Check dependencies
command -v jq >/dev/null 2>&1 || { echo >&2 "jq is required. Install with: brew install jq"; exit 1; }
command -v fswatch >/dev/null 2>&1 || { echo >&2 "fswatch is required. Install with: brew install fswatch"; exit 1; }
# Note: jq (via Homebrew) may need to be granted access under:
# System Settings → Privacy & Security → Files and Folders → jq → Removable Volumes
# Full Disk Access is not required unless you're watching protected locations (e.g. ~/Library).
# Variable to limit fswatch
DEBOUNCE_SECONDS=1.0
LAST_RUN=0
echo "Watching file: $WATCH_FILE"
[ -f "$WATCH_FILE" ] || { echo "ERROR: Watch file does not exist: $WATCH_FILE"; exit 1; }
echo "Starting fswatch loop..."
fswatch -0 "$WATCH_FILE" | while IFS= read -r -d "" event; do
NOW=$(date +%s.%N)
TIME_DIFF=$(echo "$NOW - $LAST_RUN" | bc)
# Debounce to avoid rapid repeat triggers
if (( $(echo "$TIME_DIFF < $DEBOUNCE_SECONDS" | bc -l) )); then
continue
fi
LAST_RUN=$NOW
FILE_PATH=$(jq -r '.fullpath' "$WATCH_FILE")
# Check if VS Code is already running
if ! pgrep -f "/Applications/Visual Studio Code.app/Contents/MacOS/Electron" > /dev/null; then
echo "VS Code not running. Skipping open."
continue
fi
# If VS Code is open, open the current Obsidian file
if [[ -f "$FILE_PATH" ]]; then
echo "Opening in VS Code (background): $FILE_PATH"
open -g -a "Visual Studio Code" "$FILE_PATH"
else
echo "File does not exist: $FILE_PATH"
fi
done
echo "Exited fswatch block"
Adding the Script to launchd
Finally, we set the script to run in the background using launchd, so the synchronization starts automatically at login and continues invisibly. This makes the entire setup frictionless — once configured, there’s no interaction required. Save the following XML content as a file named:
~/Library/LaunchAgents/com.<username>.watchobsidian.plist
<!-- launchd configuration file -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.<username>.watchobsidian</string>
<key>ProgramArguments</key>
<array>
<string>/Users/<username>/path/to/watch-obsidian-selection.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/watchobsidian.launchd.log</string>
<key>StandardErrorPath</key>
<string>/tmp/watchobsidian.launchd.err</string>
</dict>
</plist>
🧠Why Bother?
This setup eliminates the need to manually switch files in VS Code while navigating in Obsidian, and means when I switch from Obsidian to ChatGPT app, the file selected in VS Code (and therefore connected to ChatGPT) remains in sync. It’s a quality-of-life upgrade that supports tighter integration between the ChatGPT app and Obsidian and was a useful experiment in automating with Obsidian. At some point I might convert it to a stand-alone Obsidian extension as an exercise.