Sync local LaTeX projects with Overleaf. Bidirectional sync for .tex, .bib, images, folders. Use when pushing local changes to Overleaf, pulling updates, or checking sync status.
Bidirectional sync between local LaTeX projects and Overleaf without requiring Premium (Git access).
# Add to PATH (one-time setup)
export PATH="$PATH:/workspace/overleaf/bin"
# Link local directory to an Overleaf project
cd /workspace/overleaf/projects/my-project
overleaf link <project_id>
# Pull current state from Overleaf
overleaf pull
# Check what's changed locally
overleaf status
# Push local changes (generates Playwright steps)
overleaf push
| Command | Description |
|---|---|
overleaf link <id> | Link local directory to Overleaf project |
overleaf unlink | Remove link (keeps local files) |
overleaf status |
| Show added/modified/deleted files |
overleaf push | Generate Playwright steps to upload changes |
overleaf push --dry-run | Preview push plan without staging files |
overleaf push --no-delete | Push but skip file deletions |
overleaf pull | Download from Overleaf to local |
overleaf pull --force | Overwrite local changes |
overleaf init <name> | Initialize new project with template |
overleaf sync-complete | Mark sync as complete after push |
overleaf cleanup | Clean up staging directory |
overleaf list | List all Overleaf projects (find IDs) |
Local Project Sync Engine Overleaf
│ │ │
├── .overleaf/ │ │
│ ├── config.json │ │
│ └── sync_state.json │ │
│ │ │
└───────────────────────┼───────────────────────┤
PULL (API) │ PUSH (Playwright)
Download ZIP ←──────┤───────→ Upload files
Why Hybrid?
.tex - LaTeX source files.bib - Bibliography files.cls, .sty - Style and class files.png, .jpg, .pdf - Images and figuresEach synced project has a .overleaf/ metadata directory:
my-project/
├── main.tex
├── references.bib
├── figures/
│ └── diagram.png
└── .overleaf/ # Sync metadata (gitignored)
├── config.json # Project ID, URL, link timestamp
└── sync_state.json # File hashes from last sync
# Find project ID
overleaf list --filter "thesis"
# Create local directory and link
cd ~/Workspace/overleaf-integration/projects
mkdir thesis && cd thesis
overleaf link 507f1f77bcf86cd799439011
overleaf pull
# Edit locally...
vim main.tex
mkdir figures && cp ~/image.png figures/
# Check status
overleaf status
# → M main.tex
# → A figures/image.png
# Push changes
overleaf push
# → Generates Playwright steps for agent to execute
overleaf status
# Output:
# Project: My Thesis
# Overleaf ID: 507f1f77bcf86cd799439011
# URL: https://www.overleaf.com/project/507f1f77bcf86cd799439011
# Last sync: 2026-01-16T10:30:00 (pull)
#
# Added (2):
# + figures/diagram.png
# + appendix.tex
#
# Modified (1):
# M main.tex
overleaf push --dry-run
# Shows what would be synced without staging files
# Useful for previewing before actual push
Files must be in the user's home directory ($HOME/) for Playwright MCP to access them.
The sync engine automatically stages files to $HOME/tmp/overleaf-staging/ before upload.
Deletions are dangerous - files removed from Overleaf cannot be recovered.
--no-delete flag skips deletion operationsWhen overleaf push runs, it:
$HOME/tmp/overleaf-staging/You (the agent) must execute these steps:
--- Operation 1: create_folder ---
Target: figures
Steps: 8
--- Operation 2: upload_file ---
Target: main.tex
Steps: 11
After successful execution:
overleaf sync-complete # Updates sync state
| Request | Action |
|---|---|
| "Sync my Overleaf project" | overleaf pull then edit, then overleaf push |
| "Push my changes to Overleaf" | overleaf push (then execute Playwright steps) |
| "What files have I changed?" | overleaf status |
| "Download latest from Overleaf" | overleaf pull |
| "Link this folder to Overleaf" | overleaf link <project_id> |
| "What's the project ID for X?" | overleaf list --filter "X" |
| "Initialize a new LaTeX project" | overleaf init "Project Name" |
from overleaf_sync import OverleafProject, SyncEngine
from pathlib import Path
# Work with a project
project = OverleafProject(Path("/path/to/project"))
# Check if linked
if project.is_linked:
print(f"Linked to: {project.project_name}")
# Get status
status = project.get_status()
if status.has_changes:
print(f"Changes: {status.diff.summary()}")
# Create sync engine
engine = SyncEngine(project)
# Pull from Overleaf
result = engine.pull(force=False)
# Generate push plan
plan = engine.push(dry_run=False, skip_delete=True)
print(f"Operations: {len(plan.operations)}")
| Component | Path |
|---|---|
| CLI Script | /workspace/overleaf/scripts/overleaf_cli.py |
| Sync Engine | /workspace/overleaf/scripts/overleaf_sync.py |
| Diff Engine | /workspace/overleaf/scripts/overleaf_diff.py |
| CLI Wrapper | /workspace/overleaf/bin/overleaf |
| Projects | /workspace/overleaf/projects/ |
| Staging | $HOME/tmp/overleaf-staging/ |
Run overleaf link <project_id> first. Find project IDs with overleaf list.
Your local files have changes. Either:
overleaf pushoverleaf pull --forceRefresh cookie from browser DevTools and update .credentials file.
Playwright MCP restriction. The sync engine handles this automatically by staging files.
browser_snapshot