claude-to-overleaf 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Saeed Rezaee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,317 @@
1
+ Metadata-Version: 2.4
2
+ Name: claude-to-overleaf
3
+ Version: 0.3.0
4
+ Summary: Sync a local Git repo to an Overleaf project — designed for prompt-driven invocation by Claude Code.
5
+ Author: Saeed Rezaee
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/srezaeeucr/claude-to-overleaf
8
+ Project-URL: Repository, https://github.com/srezaeeucr/claude-to-overleaf
9
+ Project-URL: Issues, https://github.com/srezaeeucr/claude-to-overleaf/issues
10
+ Keywords: overleaf,latex,git,sync,claude,claude-code
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Operating System :: MacOS
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Software Development :: Version Control :: Git
23
+ Classifier: Topic :: Text Processing :: Markup :: LaTeX
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Dynamic: license-file
28
+
29
+ # claude-to-overleaf
30
+
31
+ [![CI](https://github.com/srezaeeucr/claude-to-overleaf/actions/workflows/ci.yml/badge.svg)](https://github.com/srezaeeucr/claude-to-overleaf/actions/workflows/ci.yml)
32
+ [![Docs](https://github.com/srezaeeucr/claude-to-overleaf/actions/workflows/docs.yml/badge.svg)](https://srezaeeucr.github.io/claude-to-overleaf/)
33
+ [![Python](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/)
34
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
35
+
36
+ 📖 **Full documentation:** <https://srezaeeucr.github.io/claude-to-overleaf/>
37
+
38
+ **One prompt to push your LaTeX repo to Overleaf. No web-UI tab-juggling. No copy-paste. No "wait, did I save that?"**
39
+
40
+ A tiny, zero-dependency Python package that ships with a [Claude Code skill](https://docs.claude.com/en/docs/claude-code/skills). Edit locally in your editor of choice, commit, then tell Claude:
41
+
42
+ > *"sync to overleaf"*
43
+
44
+ Claude invokes the skill, runs the tool, handles the safety checks, and your Overleaf project reflects the changes within seconds. (No Claude Code? It's also a normal CLI — `claude-to-overleaf sync`.)
45
+
46
+ ---
47
+
48
+ ## Why this exists
49
+
50
+ Overleaf gives every project a git URL — but actually using it day-to-day means:
51
+
52
+ - remembering to add a remote
53
+ - juggling a token you saw exactly once
54
+ - knowing the magic incantation (`commit-tree`, `HEAD^{tree}`, fast-forward push) because Overleaf rejects normal pushes
55
+ - *not* blowing away edits made in the Overleaf web editor
56
+
57
+ This script does all of that for you, and refuses to push when it would silently destroy work.
58
+
59
+ ---
60
+
61
+ ## Features
62
+
63
+ - **Ships a Claude Code skill** — one command (`claude-to-overleaf install-skill`) drops a skill file into `~/.claude/skills/` so Claude reliably knows when and how to invoke it
64
+ - **Prompt-driven by default** — say `"sync to overleaf"` and Claude does the rest, including handling the "Overleaf is ahead" cherry-pick flow
65
+ - **Standalone CLI for anyone** — `claude-to-overleaf sync` works without Claude Code
66
+ - **Installable as a real package** — `pipx install` it once and the command lives on your PATH
67
+ - **Five subcommands** — `setup`, `status`, `sync`, `pull`, `install-skill`
68
+ - **Zero runtime dependencies** — pure Python 3 stdlib (3.9+)
69
+ - **Safe by default** — refuses to push when Overleaf is ahead, or when the working tree is dirty
70
+ - **`.env`-driven config** — your token never lives in shell history or the LaTeX repo
71
+ - **Idempotent setup** — re-run anytime; only updates the remote URL if the token rotated
72
+ - **Works from anywhere** — point `REPO_PATH` at any LaTeX repo on disk
73
+
74
+ ---
75
+
76
+ ## Quick start
77
+
78
+ ### 1. Install
79
+
80
+ The recommended way — using [pipx](https://pipx.pypa.io/) so the tool gets its own isolated environment:
81
+
82
+ ```bash
83
+ pipx install claude-to-overleaf
84
+ ```
85
+
86
+ Or with regular pip:
87
+
88
+ ```bash
89
+ pip install --user claude-to-overleaf
90
+ ```
91
+
92
+ Either way, you should now have a `claude-to-overleaf` command on your PATH:
93
+
94
+ ```bash
95
+ claude-to-overleaf --help
96
+ ```
97
+
98
+ (For development: `git clone` the repo and `pip install -e .` from inside it.)
99
+
100
+ ### 1a. Install the Claude Code skill (optional, recommended)
101
+
102
+ If you use Claude Code, run this once:
103
+
104
+ ```bash
105
+ claude-to-overleaf install-skill
106
+ ```
107
+
108
+ It drops a skill file at `~/.claude/skills/claude-to-overleaf/SKILL.md`. Restart Claude Code and from then on Claude knows to use this tool whenever you say things like *"sync to overleaf"* or *"push my latex to overleaf"* — including the right way to handle "Overleaf has commits ahead" warnings (cherry-pick first, never `--force` without asking).
109
+
110
+ ### 2. Grab your Overleaf credentials
111
+
112
+ Two things from Overleaf:
113
+
114
+ | What | Where to find it |
115
+ |---|---|
116
+ | **Project ID** | Open your project → Menu (top left) → Sync → Git. The URL looks like `https://git.overleaf.com/<long-hex-id>`. The hex string is the project id. |
117
+ | **Access token** | Account Settings → Git Integration → "Generate token". Starts with `olp_`. **Overleaf only shows it once — copy immediately.** |
118
+
119
+ > **Treat the token like a password.** Anyone with it can read and write your project. Never paste it into chat, screenshots, or a tracked file. If it leaks, revoke it on Overleaf and generate a new one.
120
+
121
+ ### 3. Make a `.env`
122
+
123
+ The tool reads config from two `.env` files (CWD wins per-key, global is the base layer) plus environment variables. For a single-project setup, just put everything in one file. For a global setup:
124
+
125
+ ```bash
126
+ mkdir -p ~/.config/claude-to-overleaf
127
+ curl -fsSL https://raw.githubusercontent.com/srezaeeucr/claude-to-overleaf/main/.env.example \
128
+ > ~/.config/claude-to-overleaf/.env
129
+ ```
130
+
131
+ Then open it and fill in:
132
+
133
+ ```bash
134
+ OVERLEAF_TOKEN=olp_your_real_token
135
+ OVERLEAF_PROJECT_ID=abcdef1234567890...
136
+ REPO_PATH=/absolute/path/to/your/latex/repo
137
+ ```
138
+
139
+ The repo at `REPO_PATH` should have your `.tex` file at the **root** (e.g. `thesis.tex`, not `thesis/main.tex`). For per-project configs, drop a `.env` next to where you run the command instead.
140
+
141
+ ### 4. Wire it up
142
+
143
+ ```bash
144
+ claude-to-overleaf setup
145
+ ```
146
+
147
+ Adds an `overleaf` remote to your LaTeX repo and runs a test fetch. `OK — 'overleaf' is reachable.` means you're done.
148
+
149
+ ### 5. Sync
150
+
151
+ Edit. Commit. Push to GitHub as usual. Then either:
152
+
153
+ **With Claude Code:**
154
+
155
+ > *"sync to overleaf"*
156
+
157
+ Claude runs the tool, handles the safety checks, and reports back.
158
+
159
+ **Or run it directly:**
160
+
161
+ ```bash
162
+ claude-to-overleaf sync
163
+ ```
164
+
165
+ Refresh Overleaf in the browser — the changes are there.
166
+
167
+ ---
168
+
169
+ ## Commands
170
+
171
+ | Command | What it does |
172
+ |---|---|
173
+ | `setup` | Adds (or updates) the `overleaf` git remote and verifies auth. Idempotent — safe to re-run. |
174
+ | `status` | Shows whether local HEAD matches Overleaf. Lists Overleaf-only commits if any. |
175
+ | `sync` | Pushes local HEAD's tree to Overleaf. Refuses if Overleaf is ahead, or the working tree is dirty. |
176
+ | `sync --force` | Push anyway, overwriting Overleaf-side commits. Use deliberately. |
177
+ | `pull` | Lists Overleaf-only commits so you can `git cherry-pick` them. |
178
+ | `install-skill` | Installs the bundled Claude Code skill to `~/.claude/skills/claude-to-overleaf/`. |
179
+
180
+ Run `claude-to-overleaf --help` for the same info from the CLI. (Or `python -m claude_to_overleaf --help` if you'd rather not rely on the entry-point shim.)
181
+
182
+ ---
183
+
184
+ ## Config reference
185
+
186
+ Settings are resolved in this order, highest precedence first:
187
+
188
+ 1. Environment variables
189
+ 2. `./.env` (current working directory)
190
+ 3. `~/.config/claude-to-overleaf/.env` (global)
191
+
192
+ The two `.env` files are **merged** — values in CWD `.env` override only the keys they define. The global file is the base layer. So you can keep one shared `OVERLEAF_TOKEN` in the global file and just put `OVERLEAF_PROJECT_ID` in each repo's local `.env` for multi-project setups.
193
+
194
+ | Variable | Required | Default | Purpose |
195
+ |---|---|---|---|
196
+ | `OVERLEAF_TOKEN` | yes | — | Personal access token (starts with `olp_`) |
197
+ | `OVERLEAF_PROJECT_ID` | yes | — | Hex id from the Overleaf git URL |
198
+ | `REPO_PATH` | yes | — | Absolute path to the local git repo |
199
+ | `OVERLEAF_BRANCH` | no | `master` | Branch name on the Overleaf side |
200
+ | `OVERLEAF_REMOTE` | no | `overleaf` | What to call the remote in your repo |
201
+
202
+ ---
203
+
204
+ ## Day-to-day workflow
205
+
206
+ ### Case 1 — you only edited locally
207
+
208
+ ```bash
209
+ git add .
210
+ git commit -m "..."
211
+ git push origin main # GitHub
212
+ ```
213
+
214
+ Then ask Claude *"sync to overleaf"* — or run `claude-to-overleaf sync` directly.
215
+
216
+ ### Case 2 — someone (or you) edited on Overleaf
217
+
218
+ `sync` will refuse and tell you exactly what's there. Bring those edits home first:
219
+
220
+ ```bash
221
+ claude-to-overleaf pull # see what's on Overleaf only
222
+ cd $REPO_PATH
223
+ git cherry-pick <hash> # bring each one onto local main
224
+ claude-to-overleaf sync # now safe
225
+ ```
226
+
227
+ ### Case 3 — both sides edited the same file
228
+
229
+ Same as Case 2, but expect conflicts during cherry-pick. Resolve by hand, then:
230
+
231
+ ```bash
232
+ git add <files>
233
+ git cherry-pick --continue
234
+ ```
235
+
236
+ ---
237
+
238
+ ## Multiple Overleaf projects
239
+
240
+ Put your shared token once in `~/.config/claude-to-overleaf/.env`:
241
+
242
+ ```bash
243
+ OVERLEAF_TOKEN=olp_your_real_token
244
+ ```
245
+
246
+ Then drop a per-repo `.env` in each LaTeX project directory with just the bits that differ:
247
+
248
+ ```bash
249
+ # inside ~/repos/thesis/.env
250
+ OVERLEAF_PROJECT_ID=thesis_hex_id
251
+
252
+ # inside ~/repos/conference-paper/.env
253
+ OVERLEAF_PROJECT_ID=paper_hex_id
254
+ ```
255
+
256
+ CWD overrides global per key, so the token is inherited from the global file and the project id comes from the local one. `cd` to whichever repo you want to sync, run `claude-to-overleaf sync`. Don't forget to add `.env` to each repo's `.gitignore`.
257
+
258
+ ---
259
+
260
+ ## The one rule
261
+
262
+ **Don't edit the same file in Overleaf and locally between syncs.**
263
+
264
+ Pick one editor per file per session, sync, then switch sides. The safety check catches the common version of this mistake, but discipline beats tooling.
265
+
266
+ ---
267
+
268
+ ## Safety nets baked in
269
+
270
+ - Refuses to push when the working tree has uncommitted changes (Overleaf only sees committed state — uncommitted edits would silently not sync, leaving you confused later)
271
+ - Refuses to push when Overleaf has commits the local repo doesn't have (no silent overwrites of web-editor work)
272
+ - `.env` is in `.gitignore` so your token can't accidentally land on GitHub
273
+ - Token is URL-encoded before being embedded in the remote URL (handles weird characters cleanly)
274
+
275
+ ---
276
+
277
+ ## Troubleshooting
278
+
279
+ **`error: missing required config: OVERLEAF_TOKEN`**
280
+ The tool can't find a `.env` (it looks in CWD then `~/.config/claude-to-overleaf/`) or the key isn't in it. Re-do Step 3.
281
+
282
+ **`Authentication failed` during `setup`**
283
+ Token is wrong, expired, or revoked. Generate a new one in Overleaf, update `.env`, re-run `setup`. Sanity-check the token directly:
284
+
285
+ ```bash
286
+ curl -u git:$OVERLEAF_TOKEN -I \
287
+ "https://git.overleaf.com/$OVERLEAF_PROJECT_ID/info/refs?service=git-upload-pack"
288
+ ```
289
+
290
+ `HTTP/2 200` = token is valid. `HTTP/2 401` = bad token.
291
+
292
+ **`error: ... is not a git repo`**
293
+ `REPO_PATH` points somewhere without a `.git` directory. Fix the path or `git init` there.
294
+
295
+ **`WARNING: overleaf/master has N commit(s) not in your local repo`**
296
+ Working as designed. Run `pull`, cherry-pick what you want, then `sync` again.
297
+
298
+ ---
299
+
300
+ ## What it does under the hood
301
+
302
+ `sync` is the textbook Overleaf-git recipe in Python:
303
+
304
+ 1. `git fetch overleaf master`
305
+ 2. Compare `HEAD^{tree}` to `overleaf/master^{tree}` — exit early if equal
306
+ 3. `git commit-tree HEAD^{tree} -p overleaf/master -m "Sync from GitHub @<short>"`
307
+ 4. `git push overleaf <new-commit>:master`
308
+
309
+ The trick is step 3. Overleaf rejects non-fast-forward pushes, so the script grafts your local tree onto Overleaf's history as a brand-new commit. From Overleaf's perspective, it's a normal forward step — even though your local branch and Overleaf's branch share no recent history.
310
+
311
+ ---
312
+
313
+ ## Limitations
314
+
315
+ - Assumes your LaTeX project is at the **root** of the repo. If it lives in a subfolder, you'd need `git subtree split` instead — open an issue and we'll add it.
316
+ - One Overleaf project per `.env`. To sync multiple, drop a `.env` in each repo's directory (CWD takes precedence over the global one in `~/.config/claude-to-overleaf/`).
317
+ - Tested on macOS. Should work on Linux. Windows is unverified.
@@ -0,0 +1,289 @@
1
+ # claude-to-overleaf
2
+
3
+ [![CI](https://github.com/srezaeeucr/claude-to-overleaf/actions/workflows/ci.yml/badge.svg)](https://github.com/srezaeeucr/claude-to-overleaf/actions/workflows/ci.yml)
4
+ [![Docs](https://github.com/srezaeeucr/claude-to-overleaf/actions/workflows/docs.yml/badge.svg)](https://srezaeeucr.github.io/claude-to-overleaf/)
5
+ [![Python](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/)
6
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
7
+
8
+ 📖 **Full documentation:** <https://srezaeeucr.github.io/claude-to-overleaf/>
9
+
10
+ **One prompt to push your LaTeX repo to Overleaf. No web-UI tab-juggling. No copy-paste. No "wait, did I save that?"**
11
+
12
+ A tiny, zero-dependency Python package that ships with a [Claude Code skill](https://docs.claude.com/en/docs/claude-code/skills). Edit locally in your editor of choice, commit, then tell Claude:
13
+
14
+ > *"sync to overleaf"*
15
+
16
+ Claude invokes the skill, runs the tool, handles the safety checks, and your Overleaf project reflects the changes within seconds. (No Claude Code? It's also a normal CLI — `claude-to-overleaf sync`.)
17
+
18
+ ---
19
+
20
+ ## Why this exists
21
+
22
+ Overleaf gives every project a git URL — but actually using it day-to-day means:
23
+
24
+ - remembering to add a remote
25
+ - juggling a token you saw exactly once
26
+ - knowing the magic incantation (`commit-tree`, `HEAD^{tree}`, fast-forward push) because Overleaf rejects normal pushes
27
+ - *not* blowing away edits made in the Overleaf web editor
28
+
29
+ This script does all of that for you, and refuses to push when it would silently destroy work.
30
+
31
+ ---
32
+
33
+ ## Features
34
+
35
+ - **Ships a Claude Code skill** — one command (`claude-to-overleaf install-skill`) drops a skill file into `~/.claude/skills/` so Claude reliably knows when and how to invoke it
36
+ - **Prompt-driven by default** — say `"sync to overleaf"` and Claude does the rest, including handling the "Overleaf is ahead" cherry-pick flow
37
+ - **Standalone CLI for anyone** — `claude-to-overleaf sync` works without Claude Code
38
+ - **Installable as a real package** — `pipx install` it once and the command lives on your PATH
39
+ - **Five subcommands** — `setup`, `status`, `sync`, `pull`, `install-skill`
40
+ - **Zero runtime dependencies** — pure Python 3 stdlib (3.9+)
41
+ - **Safe by default** — refuses to push when Overleaf is ahead, or when the working tree is dirty
42
+ - **`.env`-driven config** — your token never lives in shell history or the LaTeX repo
43
+ - **Idempotent setup** — re-run anytime; only updates the remote URL if the token rotated
44
+ - **Works from anywhere** — point `REPO_PATH` at any LaTeX repo on disk
45
+
46
+ ---
47
+
48
+ ## Quick start
49
+
50
+ ### 1. Install
51
+
52
+ The recommended way — using [pipx](https://pipx.pypa.io/) so the tool gets its own isolated environment:
53
+
54
+ ```bash
55
+ pipx install claude-to-overleaf
56
+ ```
57
+
58
+ Or with regular pip:
59
+
60
+ ```bash
61
+ pip install --user claude-to-overleaf
62
+ ```
63
+
64
+ Either way, you should now have a `claude-to-overleaf` command on your PATH:
65
+
66
+ ```bash
67
+ claude-to-overleaf --help
68
+ ```
69
+
70
+ (For development: `git clone` the repo and `pip install -e .` from inside it.)
71
+
72
+ ### 1a. Install the Claude Code skill (optional, recommended)
73
+
74
+ If you use Claude Code, run this once:
75
+
76
+ ```bash
77
+ claude-to-overleaf install-skill
78
+ ```
79
+
80
+ It drops a skill file at `~/.claude/skills/claude-to-overleaf/SKILL.md`. Restart Claude Code and from then on Claude knows to use this tool whenever you say things like *"sync to overleaf"* or *"push my latex to overleaf"* — including the right way to handle "Overleaf has commits ahead" warnings (cherry-pick first, never `--force` without asking).
81
+
82
+ ### 2. Grab your Overleaf credentials
83
+
84
+ Two things from Overleaf:
85
+
86
+ | What | Where to find it |
87
+ |---|---|
88
+ | **Project ID** | Open your project → Menu (top left) → Sync → Git. The URL looks like `https://git.overleaf.com/<long-hex-id>`. The hex string is the project id. |
89
+ | **Access token** | Account Settings → Git Integration → "Generate token". Starts with `olp_`. **Overleaf only shows it once — copy immediately.** |
90
+
91
+ > **Treat the token like a password.** Anyone with it can read and write your project. Never paste it into chat, screenshots, or a tracked file. If it leaks, revoke it on Overleaf and generate a new one.
92
+
93
+ ### 3. Make a `.env`
94
+
95
+ The tool reads config from two `.env` files (CWD wins per-key, global is the base layer) plus environment variables. For a single-project setup, just put everything in one file. For a global setup:
96
+
97
+ ```bash
98
+ mkdir -p ~/.config/claude-to-overleaf
99
+ curl -fsSL https://raw.githubusercontent.com/srezaeeucr/claude-to-overleaf/main/.env.example \
100
+ > ~/.config/claude-to-overleaf/.env
101
+ ```
102
+
103
+ Then open it and fill in:
104
+
105
+ ```bash
106
+ OVERLEAF_TOKEN=olp_your_real_token
107
+ OVERLEAF_PROJECT_ID=abcdef1234567890...
108
+ REPO_PATH=/absolute/path/to/your/latex/repo
109
+ ```
110
+
111
+ The repo at `REPO_PATH` should have your `.tex` file at the **root** (e.g. `thesis.tex`, not `thesis/main.tex`). For per-project configs, drop a `.env` next to where you run the command instead.
112
+
113
+ ### 4. Wire it up
114
+
115
+ ```bash
116
+ claude-to-overleaf setup
117
+ ```
118
+
119
+ Adds an `overleaf` remote to your LaTeX repo and runs a test fetch. `OK — 'overleaf' is reachable.` means you're done.
120
+
121
+ ### 5. Sync
122
+
123
+ Edit. Commit. Push to GitHub as usual. Then either:
124
+
125
+ **With Claude Code:**
126
+
127
+ > *"sync to overleaf"*
128
+
129
+ Claude runs the tool, handles the safety checks, and reports back.
130
+
131
+ **Or run it directly:**
132
+
133
+ ```bash
134
+ claude-to-overleaf sync
135
+ ```
136
+
137
+ Refresh Overleaf in the browser — the changes are there.
138
+
139
+ ---
140
+
141
+ ## Commands
142
+
143
+ | Command | What it does |
144
+ |---|---|
145
+ | `setup` | Adds (or updates) the `overleaf` git remote and verifies auth. Idempotent — safe to re-run. |
146
+ | `status` | Shows whether local HEAD matches Overleaf. Lists Overleaf-only commits if any. |
147
+ | `sync` | Pushes local HEAD's tree to Overleaf. Refuses if Overleaf is ahead, or the working tree is dirty. |
148
+ | `sync --force` | Push anyway, overwriting Overleaf-side commits. Use deliberately. |
149
+ | `pull` | Lists Overleaf-only commits so you can `git cherry-pick` them. |
150
+ | `install-skill` | Installs the bundled Claude Code skill to `~/.claude/skills/claude-to-overleaf/`. |
151
+
152
+ Run `claude-to-overleaf --help` for the same info from the CLI. (Or `python -m claude_to_overleaf --help` if you'd rather not rely on the entry-point shim.)
153
+
154
+ ---
155
+
156
+ ## Config reference
157
+
158
+ Settings are resolved in this order, highest precedence first:
159
+
160
+ 1. Environment variables
161
+ 2. `./.env` (current working directory)
162
+ 3. `~/.config/claude-to-overleaf/.env` (global)
163
+
164
+ The two `.env` files are **merged** — values in CWD `.env` override only the keys they define. The global file is the base layer. So you can keep one shared `OVERLEAF_TOKEN` in the global file and just put `OVERLEAF_PROJECT_ID` in each repo's local `.env` for multi-project setups.
165
+
166
+ | Variable | Required | Default | Purpose |
167
+ |---|---|---|---|
168
+ | `OVERLEAF_TOKEN` | yes | — | Personal access token (starts with `olp_`) |
169
+ | `OVERLEAF_PROJECT_ID` | yes | — | Hex id from the Overleaf git URL |
170
+ | `REPO_PATH` | yes | — | Absolute path to the local git repo |
171
+ | `OVERLEAF_BRANCH` | no | `master` | Branch name on the Overleaf side |
172
+ | `OVERLEAF_REMOTE` | no | `overleaf` | What to call the remote in your repo |
173
+
174
+ ---
175
+
176
+ ## Day-to-day workflow
177
+
178
+ ### Case 1 — you only edited locally
179
+
180
+ ```bash
181
+ git add .
182
+ git commit -m "..."
183
+ git push origin main # GitHub
184
+ ```
185
+
186
+ Then ask Claude *"sync to overleaf"* — or run `claude-to-overleaf sync` directly.
187
+
188
+ ### Case 2 — someone (or you) edited on Overleaf
189
+
190
+ `sync` will refuse and tell you exactly what's there. Bring those edits home first:
191
+
192
+ ```bash
193
+ claude-to-overleaf pull # see what's on Overleaf only
194
+ cd $REPO_PATH
195
+ git cherry-pick <hash> # bring each one onto local main
196
+ claude-to-overleaf sync # now safe
197
+ ```
198
+
199
+ ### Case 3 — both sides edited the same file
200
+
201
+ Same as Case 2, but expect conflicts during cherry-pick. Resolve by hand, then:
202
+
203
+ ```bash
204
+ git add <files>
205
+ git cherry-pick --continue
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Multiple Overleaf projects
211
+
212
+ Put your shared token once in `~/.config/claude-to-overleaf/.env`:
213
+
214
+ ```bash
215
+ OVERLEAF_TOKEN=olp_your_real_token
216
+ ```
217
+
218
+ Then drop a per-repo `.env` in each LaTeX project directory with just the bits that differ:
219
+
220
+ ```bash
221
+ # inside ~/repos/thesis/.env
222
+ OVERLEAF_PROJECT_ID=thesis_hex_id
223
+
224
+ # inside ~/repos/conference-paper/.env
225
+ OVERLEAF_PROJECT_ID=paper_hex_id
226
+ ```
227
+
228
+ CWD overrides global per key, so the token is inherited from the global file and the project id comes from the local one. `cd` to whichever repo you want to sync, run `claude-to-overleaf sync`. Don't forget to add `.env` to each repo's `.gitignore`.
229
+
230
+ ---
231
+
232
+ ## The one rule
233
+
234
+ **Don't edit the same file in Overleaf and locally between syncs.**
235
+
236
+ Pick one editor per file per session, sync, then switch sides. The safety check catches the common version of this mistake, but discipline beats tooling.
237
+
238
+ ---
239
+
240
+ ## Safety nets baked in
241
+
242
+ - Refuses to push when the working tree has uncommitted changes (Overleaf only sees committed state — uncommitted edits would silently not sync, leaving you confused later)
243
+ - Refuses to push when Overleaf has commits the local repo doesn't have (no silent overwrites of web-editor work)
244
+ - `.env` is in `.gitignore` so your token can't accidentally land on GitHub
245
+ - Token is URL-encoded before being embedded in the remote URL (handles weird characters cleanly)
246
+
247
+ ---
248
+
249
+ ## Troubleshooting
250
+
251
+ **`error: missing required config: OVERLEAF_TOKEN`**
252
+ The tool can't find a `.env` (it looks in CWD then `~/.config/claude-to-overleaf/`) or the key isn't in it. Re-do Step 3.
253
+
254
+ **`Authentication failed` during `setup`**
255
+ Token is wrong, expired, or revoked. Generate a new one in Overleaf, update `.env`, re-run `setup`. Sanity-check the token directly:
256
+
257
+ ```bash
258
+ curl -u git:$OVERLEAF_TOKEN -I \
259
+ "https://git.overleaf.com/$OVERLEAF_PROJECT_ID/info/refs?service=git-upload-pack"
260
+ ```
261
+
262
+ `HTTP/2 200` = token is valid. `HTTP/2 401` = bad token.
263
+
264
+ **`error: ... is not a git repo`**
265
+ `REPO_PATH` points somewhere without a `.git` directory. Fix the path or `git init` there.
266
+
267
+ **`WARNING: overleaf/master has N commit(s) not in your local repo`**
268
+ Working as designed. Run `pull`, cherry-pick what you want, then `sync` again.
269
+
270
+ ---
271
+
272
+ ## What it does under the hood
273
+
274
+ `sync` is the textbook Overleaf-git recipe in Python:
275
+
276
+ 1. `git fetch overleaf master`
277
+ 2. Compare `HEAD^{tree}` to `overleaf/master^{tree}` — exit early if equal
278
+ 3. `git commit-tree HEAD^{tree} -p overleaf/master -m "Sync from GitHub @<short>"`
279
+ 4. `git push overleaf <new-commit>:master`
280
+
281
+ The trick is step 3. Overleaf rejects non-fast-forward pushes, so the script grafts your local tree onto Overleaf's history as a brand-new commit. From Overleaf's perspective, it's a normal forward step — even though your local branch and Overleaf's branch share no recent history.
282
+
283
+ ---
284
+
285
+ ## Limitations
286
+
287
+ - Assumes your LaTeX project is at the **root** of the repo. If it lives in a subfolder, you'd need `git subtree split` instead — open an issue and we'll add it.
288
+ - One Overleaf project per `.env`. To sync multiple, drop a `.env` in each repo's directory (CWD takes precedence over the global one in `~/.config/claude-to-overleaf/`).
289
+ - Tested on macOS. Should work on Linux. Windows is unverified.
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "claude-to-overleaf"
7
+ dynamic = ["version"]
8
+ description = "Sync a local Git repo to an Overleaf project — designed for prompt-driven invocation by Claude Code."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Saeed Rezaee" }]
14
+ keywords = ["overleaf", "latex", "git", "sync", "claude", "claude-code"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Environment :: Console",
18
+ "Intended Audience :: Science/Research",
19
+ "Operating System :: MacOS",
20
+ "Operating System :: POSIX :: Linux",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3 :: Only",
23
+ "Programming Language :: Python :: 3.9",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Topic :: Software Development :: Version Control :: Git",
28
+ "Topic :: Text Processing :: Markup :: LaTeX",
29
+ ]
30
+ # No runtime dependencies — pure stdlib.
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/srezaeeucr/claude-to-overleaf"
34
+ Repository = "https://github.com/srezaeeucr/claude-to-overleaf"
35
+ Issues = "https://github.com/srezaeeucr/claude-to-overleaf/issues"
36
+
37
+ [project.scripts]
38
+ claude-to-overleaf = "claude_to_overleaf.cli:main"
39
+
40
+ [tool.setuptools.packages.find]
41
+ where = ["src"]
42
+
43
+ [tool.setuptools.package-data]
44
+ claude_to_overleaf = ["SKILL.md"]
45
+
46
+ [tool.setuptools.dynamic]
47
+ version = { attr = "claude_to_overleaf.__version__" }
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+