overleaf-review 0.2.1 → 0.4.0

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.
Files changed (3) hide show
  1. package/README.md +120 -12
  2. package/dist/cli.js +3254 -308
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -35,13 +35,52 @@ workflow and carries the review layer Git can't represent.
35
35
  - 📥 **`pull`** — read comments + tracked changes (with anchors) into a Git-friendly sidecar
36
36
  (`.overleaf/reviews.md` + `.json`), so your tools have every co-author note in context.
37
37
  - 📤 **`push`** — turn local edits into **tracked-change suggestions**, mapping files to Overleaf
38
- docs by path (one file, or every changed `.tex` at once). `--dry-run` previews the exact ops.
38
+ docs by path (one file, or every changed `.tex` at once). A saved Base/Local/Live merge keeps
39
+ non-overlapping co-author edits, while overlapping edits and active tracked-change ranges stop
40
+ the push for inspection. `--direct` sends plain edits instead of suggestions.
41
+ - 🔄 **`fetch`** — write Overleaf's current text back down into your repo (read-only on Overleaf, so
42
+ it cannot disturb a single comment or tracked change) and save the synchronization base used by
43
+ future pushes. Existing local files are snapshotted before replacement.
44
+ - 📋 **`review plan` / `review submit`** — save a complete, binding push plan, inspect it, then
45
+ submit it only if the local file, saved base, live document version, and review ranges are still
46
+ unchanged.
47
+ - 🖼️ **`upload`** — push figures, PDFs, or new files into Overleaf.
39
48
  - 💬 **`comment` / `reply` / `resolve` / `delete-comment` / `delete-message`** — full comment
40
49
  control: start a thread, reply, resolve/reopen, delete a whole thread or a single message.
41
- - **`accept` / `reject`** act on your collaborators' tracked changes from the CLI.
50
+ Identical recent comments and replies are retry-safe by default; `--force` deliberately posts
51
+ another copy.
52
+ - ✅ **`accept` / `reject`** — act on collaborators' tracked changes and verify removal of every
53
+ requested change id before reporting success; rejection also verifies the exact resulting text.
54
+ - 🧾 **Review receipts** — pushes, accept/reject actions, comments, and replies leave durable JSON
55
+ records under `.overleaf/receipts/` showing what was verified or left ambiguous.
42
56
  - 🔑 **`login`** — validated auth stored outside your repo (chmod 600); `--browser` mode is
43
57
  institutional-SSO friendly.
44
58
 
59
+ ## ⚠️ Don't mix this with Overleaf's Git/GitHub sync
60
+
61
+ **Overleaf's Git integration writes documents by wholesale content replacement.** The review layer
62
+ is stored separately, anchored by character offsets — so a bulk overwrite orphans or displaces your
63
+ comments and tracked changes. (Overleaf's own docs advise against combining Git with track changes.)
64
+ This isn't something a tool can patch around; it's inherent to how the bridge writes.
65
+
66
+ `overleaf-review` writes through Overleaf's **real-time OT API** instead — incremental insert/delete
67
+ ops that Overleaf *transforms the review ranges against*, so comments and tracked changes survive.
68
+
69
+ **Recommended setup: unlink Overleaf's Git/GitHub sync and let `overleaf-review` be the only bridge.**
70
+
71
+ | Need | Command |
72
+ | --- | --- |
73
+ | Begin a local review session | `review start` |
74
+ | Overleaf text → your repo and save Base | `fetch` |
75
+ | Your edits → Overleaf, as suggestions | `push` |
76
+ | Your edits → Overleaf, directly | `push --direct` |
77
+ | Inspect, then submit the exact same change | `review plan --out plan.json`, then `review submit --plan plan.json` |
78
+ | Figures / new files → Overleaf | `upload` |
79
+
80
+ Your Git repo stays a completely normal repo — commit whatever you like, `.tex` included — and
81
+ nothing bidirectional exists that can clobber the review record. (Renaming/deleting files is still
82
+ done in the Overleaf UI.)
83
+
45
84
  ## 📦 Install
46
85
 
47
86
  ```bash
@@ -58,14 +97,28 @@ overleaf-review login --browser # opens your Chrome, log in normally (SSO work
58
97
  # 2. Link this repo to an Overleaf project (id from the project URL)
59
98
  overleaf-review link --project 6a4c…bec5a
60
99
 
61
- # 3. Sync the review layer
62
- overleaf-review pull # comments + changes .overleaf/
63
- overleaf-review push --dry-run # preview local edits as suggestions
64
- overleaf-review push # send them as tracked changes
100
+ # 3. Start from Overleaf's current text and review state
101
+ overleaf-review review start # fetch text + save Base + pull reviews
102
+
103
+ # 4. Edit locally, then inspect and submit a binding plan
104
+ overleaf-review review plan --out .overleaf/push-plan.json
105
+ overleaf-review review submit --plan .overleaf/push-plan.json
106
+
107
+ # Or use the safe one-shot form when a separate approval step is unnecessary
108
+ overleaf-review push --dry-run # transient preview; sends nothing
109
+ overleaf-review push # plan, validate, send, and verify
110
+
111
+ # 5. Work with review threads
65
112
  overleaf-review comment --anchor "Introduction" --message "Expand this section"
66
113
  overleaf-review resolve --thread <id> # thread ids come from `pull`
67
114
  ```
68
115
 
116
+ `fetch` and `review start` write Overleaf's text into the local files, so commit or otherwise
117
+ preserve local-only work before running them. They save `.overleaf/base.json`; edit only after that
118
+ base is established. Existing installations should run one of these commands once before their
119
+ first safe push. If an upgraded repository already has unpushed edits, copy or stash them, run
120
+ `fetch`, then restore the edits so the new Base remains the Overleaf version they were based on.
121
+
69
122
  ## 🧭 Commands
70
123
 
71
124
  | Command | What it does |
@@ -73,9 +126,14 @@ overleaf-review resolve --thread <id> # thread ids come from `pull`
73
126
  | `login [--cookie <v>] [--browser]` | Authenticate and store your session (SSO-friendly `--browser`) |
74
127
  | `link --project <id>` | Link this repo to an Overleaf project (`.overleaf/config.json`) |
75
128
  | `pull [--out <dir>]` | Read comments + tracked changes into a sidecar |
76
- | `push [--file <f>] [--doc <name>] [--dry-run]` | Send local edits as tracked-change suggestions (all changed `.tex` if no `--file`) |
77
- | `comment --anchor <text> --message <text> [--doc <name>] [--nth <n>]` | Add a comment anchored on the given text |
78
- | `reply --thread <id> --message <text>` | Reply to an existing comment thread |
129
+ | `fetch [--file <f>] [--dry-run]` | Write Overleaf text locally, snapshot replaced files, and save Base (read-only on Overleaf) |
130
+ | `review start [--file <f>] [--out <dir>]` | Fetch text, save the synchronization base, and pull the review sidecar |
131
+ | `review plan --out <plan.json> [--file <f>] [--doc <name>] [--direct] [--allow-overlap] [--unsafe-no-base]` | Create a complete binding plan without changing Overleaf |
132
+ | `review submit --plan <plan.json> [--acknowledge-ambiguous]` | Submit that plan only if all recorded preconditions still match |
133
+ | `upload <path…> [--folder <name>]` | Upload figures / new files into Overleaf |
134
+ | `push [--file <f>] [--doc <name>] [--direct] [--dry-run] [--plan-out <path>] [--plan <path>] [--allow-overlap] [--unsafe-no-base] [--acknowledge-ambiguous]` | Safely merge and send local edits (all changed `.tex` if no `--file`) |
135
+ | `comment --anchor <text> --message <text> [--doc <name>] [--nth <n>] [--force]` | Add an anchored comment; recent identical retries are skipped unless forced |
136
+ | `reply --thread <id> --message <text> [--force]` | Reply to a thread; recent identical retries are skipped unless forced |
79
137
  | `resolve --thread <id> [--reopen]` | Resolve (or reopen) a comment thread |
80
138
  | `delete-comment --thread <id>` | Delete a whole comment thread |
81
139
  | `delete-message --message-id <id> [--thread <id>]` | Delete a single message within a thread |
@@ -84,6 +142,52 @@ overleaf-review resolve --thread <id> # thread ids come from `pull`
84
142
 
85
143
  Thread and change ids are listed by `pull` (in `.overleaf/reviews.md`).
86
144
 
145
+ ### Safe pushes
146
+
147
+ A normal push compares three versions of each document:
148
+
149
+ - **Base** — the Overleaf text saved by the last `fetch` or successful push;
150
+ - **Local** — your current file;
151
+ - **Live** — the document as it exists in Overleaf when planning.
152
+
153
+ Your Base→Local intent is rebased onto Live. Non-overlapping live edits are preserved; genuinely
154
+ overlapping Base→Local and Base→Live edits abort instead of silently undoing a collaborator's
155
+ work. A proposed edit that intersects an active tracked-change range is also blocked by default and
156
+ the relevant change ids are listed.
157
+
158
+ Use `--allow-overlap` only after inspecting those tracked changes. `--unsafe-no-base` is a deliberate
159
+ legacy escape hatch that treats the current Live document as Base; it loses the protection against
160
+ co-author edits made since your local file was obtained. `--direct` changes how the validated ops
161
+ are represented in Overleaf, not the merge and conflict checks.
162
+
163
+ For a separate approval step, `review plan --out plan.json` stores the full operation list and its
164
+ preconditions, active tracked ranges, and any comment overlaps. `review submit --plan plan.json`
165
+ re-derives the plan from Base/Local/Live and aborts if preflight finds that the project, local source,
166
+ synchronization base, live document version/content, or active review ranges changed. Plans contain
167
+ manuscript fragments, so store or commit them with the same care as the source itself. `push
168
+ --plan-out <path>` and `push --plan <path>` expose the same workflow through the one-level command.
169
+
170
+ Overleaf can transform an update that races with a collaborator after the final preflight. The tool
171
+ therefore reads every result back and fails rather than claiming success when the exact planned text
172
+ is not present. A multi-file submission is not transactional; if a later file fails, its receipt
173
+ identifies every earlier file already verified on Overleaf.
174
+
175
+ Pushes, accept/reject actions, comments, and replies write audit receipts beneath
176
+ `.overleaf/receipts/`. Keep an ambiguous receipt: it means the request may have reached Overleaf but
177
+ readback could not prove the result, so inspect the live project before retrying. For comments and
178
+ replies, an identical recent message is treated as a retry and skipped; pass `--force` only when a
179
+ second identical message is intentional.
180
+
181
+ An ambiguous push quarantines its affected documents even if a newly generated plan has a different
182
+ timestamp or tracked-change seed. After waiting for delayed updates and manually reconciling the
183
+ receipt against live Overleaf, `--acknowledge-ambiguous` explicitly clears that submission-time
184
+ guard; it should never be used as an automatic retry flag.
185
+
186
+ Mutating commands are serialized by a working-tree-specific lock in the system temporary directory,
187
+ preventing two local agents from racing through the same preflight. A normal exit removes the lock.
188
+ If a process crashes and leaves it behind, the next command prints its exact path; inspect any
189
+ relevant receipt and the live Overleaf document before removing it manually.
190
+
87
191
  ## 🧠 How it works
88
192
 
89
193
  overleaf.com's editor speaks an old **socket.io 0.9** protocol over a WebSocket. The client
@@ -93,8 +197,12 @@ overleaf.com's editor speaks an old **socket.io 0.9** protocol over a WebSocket.
93
197
  - a **tracked change** is an insert/delete op with a `meta.tc` flag;
94
198
  - a **comment** is a `c` op plus a REST post of the message text.
95
199
 
96
- `push` diffs your local file against Overleaf's current content and translates the hunks into
97
- sequential OT ops, so your edits land as reviewable suggestions rather than silent changes.
200
+ `push` performs the Base/Local/Live merge described above, translates Live→merged text into
201
+ sequential OT ops, and waits for Overleaf's applied-update event. It then reads the document back
202
+ and verifies the final text—and, in suggestion mode, tracked-change ids—before updating the saved
203
+ base. Accept and reject operations verify that the requested ids disappeared; rejection also
204
+ verifies the exact planned text. Push, accept/reject, comment, and reply fail loudly when their
205
+ readback cannot prove success.
98
206
 
99
207
  ## ⚠️ Disclaimer
100
208
 
@@ -105,7 +213,7 @@ own account and projects. Use at your own risk.
105
213
 
106
214
  ## 🗺️ Roadmap
107
215
 
108
- - A `pull` that also writes doc content (not just the review sidecar)
216
+ - File rename / delete (currently done in the Overleaf UI)
109
217
  - Trusted-publishing CI
110
218
 
111
219
  ## 📝 Changelog