@eddie-doc/cli 0.0.0-stage → 1.4.4

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.
package/README.md CHANGED
@@ -1,3 +1,782 @@
1
- # Temporary Holding Version
1
+ # @eddie-doc/cli
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Map an editor's PDF annotations back onto your AsciiDoc source, from the
4
+ command line.**
5
+
6
+ You write in AsciiDoc, render a PDF, and send it to an editor. It comes back
7
+ covered in highlights, strikethroughs, sticky notes and insert marks. `eddie`
8
+ reads those marks, finds the exact line each one refers to in your `.adoc`
9
+ source, and turns them into review items you can list, answer, resolve and
10
+ apply. It can then write the review back into a freshly rendered PDF, so the
11
+ editor sees your answers next to their own marks.
12
+
13
+ This is the command line of [Eddie Doc](https://volland.github.io/eddie-doc/).
14
+ It runs the same core as the
15
+ [VS Code extension](https://marketplace.visualstudio.com/items?itemName=pavlyshyn.eddie-doc)
16
+ and the [Obsidian plugin](https://github.com/Volland/eddie-doc/blob/main/docs/OBSIDIAN.md),
17
+ and reads and writes the same review files. A review started in the editor can
18
+ be finished in a terminal, a CI job or an AI agent, and the other way round.
19
+
20
+ ```console
21
+ $ eddie open chapter-04.adoc ~/Downloads/chapter-04-edited.pdf --origin "Acme Copyedit"
22
+ .eddie/manuscript/chapter-04/rev-1/chapter-04-edited.review.json
23
+ Revision 1 · Acme Copyedit — 31 annotation(s), 29 mapped to source
24
+
25
+ $ eddie list chapter-04.adoc --bucket open
26
+ Open (24)
27
+ #1 AC [Delete] p2 L14 1.00 marker Cut — repeats the intro.
28
+ #2 AC [Highlight] p2 L19 0.94 fuzzy Reword: 'trust or lineage'.
29
+ …
30
+ ```
31
+
32
+ ## Contents
33
+
34
+ - [Install](#install)
35
+ - [Quick start](#quick-start)
36
+ - [Concepts](#concepts)
37
+ - [Choosing a document, mapping and annotation](#choosing-a-document-mapping-and-annotation)
38
+ - [Command reference](#command-reference)
39
+ - [Configuration](#configuration)
40
+ - [Scripting: JSON output and exit codes](#scripting-json-output-and-exit-codes)
41
+ - [Recipes](#recipes)
42
+ - [Working with VS Code and Obsidian](#working-with-vs-code-and-obsidian)
43
+ - [Troubleshooting](#troubleshooting)
44
+
45
+ ## Install
46
+
47
+ Node.js 18 or newer. The package has no runtime dependencies: PDF reading
48
+ (pdf.js) and PDF writing (pdf-lib) are bundled into it.
49
+
50
+ ```bash
51
+ npm install -g @eddie-doc/cli # installs `eddie` (and the long name `eddie-doc`)
52
+ eddie help
53
+
54
+ npx @eddie-doc/cli help # or run it without installing
55
+ ```
56
+
57
+ As a project dependency, so a build script pins the version:
58
+
59
+ ```bash
60
+ npm install --save-dev @eddie-doc/cli
61
+ npx eddie stamp build/ch04.pdf src/ch04.adoc
62
+ ```
63
+
64
+ **Already using the VS Code extension?** It ships this CLI. `eddie` is on the
65
+ PATH of every VS Code integrated terminal, and the command
66
+ **Eddie Doc: Install 'eddie' Command in PATH** links it into `~/.local/bin` for
67
+ other shells. That copy runs on VS Code's own runtime, so it needs no Node
68
+ install. See [Working with VS Code and Obsidian](#working-with-vs-code-and-obsidian).
69
+
70
+ ## Quick start
71
+
72
+ ```bash
73
+ # 1. Map the editor's annotated PDF onto your source.
74
+ eddie open src/ch04.adoc ~/Downloads/ch04-acme.pdf --origin "Acme" --type copyedit
75
+
76
+ # 2. See what came back, grouped the way the editor's tree groups it.
77
+ eddie list src/ch04.adoc
78
+
79
+ # 3. Read one remark in context, with the source lines it points at.
80
+ eddie show src/ch04.adoc 7
81
+
82
+ # 4. Answer, resolve, or fix it.
83
+ eddie reply src/ch04.adoc 7 "Reworded — see the new second sentence."
84
+ eddie resolve src/ch04.adoc 7 8 12
85
+ eddie apply src/ch04.adoc --dry-run # every confident delete/replace/insert
86
+ eddie apply src/ch04.adoc --yes
87
+
88
+ # 5. Link what the matcher could not place.
89
+ eddie triage src/ch04.adoc -i
90
+
91
+ # 6. Send it back: a report, and the review written into a fresh render.
92
+ eddie report src/ch04.adoc
93
+ asciidoctor-pdf -o build/ch04.pdf src/ch04.adoc
94
+ eddie stamp build/ch04.pdf src/ch04.adoc
95
+ ```
96
+
97
+ Every step writes to the review folder (`.eddie/` by default). Your manuscript
98
+ changes only when you ask: `apply`, `fix`, `anchor` and `strip`. The exception
99
+ is `open`, which adds invisible anchor comments unless you pass `--no-anchor`;
100
+ see [Anchors](#anchors).
101
+
102
+ ## Concepts
103
+
104
+ ### Source, annotated PDF, clean render
105
+
106
+ - **Source**: the `.adoc` file you write.
107
+ - **Annotated PDF**: a PDF rendered from the source that an editor marked up in
108
+ Acrobat, Preview, PDF Expert, Zotero or similar. Highlights, strikeouts,
109
+ underlines, sticky notes, free-text notes, carets (insert marks) and
110
+ replacement marks are read.
111
+ - **Clean render**: a new PDF built from the current source, with no marks.
112
+ `stamp` writes the review into a copy of it and never modifies the original.
113
+
114
+ ### Mapping
115
+
116
+ One annotated PDF mapped onto one source is a **mapping**. It is stored as a
117
+ JSON **sidecar** file, `<mapping-id>.review.json`, which holds every annotation,
118
+ where it was matched in the source, and your state: resolved flags, replies,
119
+ hand-made links and anchors. The id comes from the PDF's file name
120
+ (`ch04-acme.annotated.pdf` → `ch04-acme`).
121
+
122
+ More PDFs can be added to an existing mapping with `append`, for example when
123
+ the editor sends the chapter again with more marks. Marks the mapping already
124
+ has are skipped, and new ones continue its numbering.
125
+
126
+ ### Rounds
127
+
128
+ A **round** (also called a revision) groups the mappings that came back from one
129
+ pass over the manuscript. Two editors marking the same draft give two mappings
130
+ in one round. When the next draft comes back, `open --new-round` starts round 2
131
+ and carries your resolved flags, replies and links forward by matching each
132
+ remark's content, because a re-exported PDF changes every annotation's internal
133
+ id.
134
+
135
+ ### The review folder
136
+
137
+ Everything goes under one folder at the workspace root, laid out to mirror the
138
+ manuscript, so the manuscript folder itself never gains a file:
139
+
140
+ ```text
141
+ manuscript/chapter-04.adoc ← your source
142
+
143
+ .eddie/manuscript/chapter-04/
144
+ rev-1/
145
+ acme.review.json ← one mapping
146
+ acme.review.md ← its exported report
147
+ pdf/acme.pdf ← imported PDF (opt-in) and stamped PDFs
148
+ rev-2/
149
+ acme.review.json
150
+ beta-proofread.review.json ← a second reviewer in the same round
151
+ ```
152
+
153
+ Set `reviewFolder` to `""` to use the old layout instead, with
154
+ `<file>.review.json` beside each `.adoc`. Paths inside a sidecar are relative to
155
+ the sidecar, so the folder can be moved or committed as it is.
156
+
157
+ ### Buckets
158
+
159
+ Every annotation is in exactly one group, as in the editors:
160
+
161
+ | Bucket | Meaning |
162
+ | --- | --- |
163
+ | `open` | Placed in the source with confidence; waiting for you. |
164
+ | `review` | Placed, but the match is below `highConfidence`, or **stale** (the text it was linked to has changed). Check it, then `confirm` or `relink`. |
165
+ | `unmatched` | No place found. Link it with `triage` or `relink`. |
166
+ | `resolved` | Done. |
167
+
168
+ ### Remark numbers
169
+
170
+ Each annotation gets a number when it is first mapped, shown as `#7 AC` (number
171
+ and the reviewer's initials). The number never changes, so you and the editor can
172
+ refer to "#7" across emails, reports and rounds. Most commands accept it as
173
+ `7` or `#7`.
174
+
175
+ ### Anchors
176
+
177
+ When a PDF is mapped, the source text still matches what the editor read, which
178
+ is the most reliable moment for matching. By default `open` then writes an
179
+ AsciiDoc comment line above each marked paragraph:
180
+
181
+ ```asciidoc
182
+ // eddie:c673e3ed
183
+ A knowledge graph is a structured representation of information where entities
184
+ are connected by well-defined relationships.
185
+ ```
186
+
187
+ Asciidoctor drops comment lines, so markers never appear in the rendered output.
188
+ They keep each annotation attached to its paragraph when you rewrite the text,
189
+ including edits made outside any Eddie tool. Without markers, re-matching falls
190
+ back to block ids, block fingerprints and text similarity. `eddie strip` removes
191
+ every marker; `--no-anchor` (or `autoAnchor: false`) skips writing them.
192
+
193
+ ### Match methods
194
+
195
+ `show` and `list` print how each annotation was placed, most trusted first:
196
+
197
+ | Method | How |
198
+ | --- | --- |
199
+ | `marker` | Its `// eddie:` comment was found. |
200
+ | `blockId` | The enclosing `[#id]` block was found. |
201
+ | `fingerprint` | The enclosing block's content hash still matches. |
202
+ | `fuzzy` | Token similarity to the text under the mark. |
203
+ | `lexical` | Character-trigram similarity; finds words that were inflected, hyphenated or misspelled. |
204
+ | `semantic` | Embedding similarity, through a local Ollama server (opt-in). |
205
+ | `manual` | You linked it by hand. |
206
+
207
+ ## Choosing a document, mapping and annotation
208
+
209
+ Most commands take the **source** as their first argument: `eddie list ch04.adoc`.
210
+
211
+ A source with several mappings: commands act on the **newest** one (shown with
212
+ `*` in `eddie rounds`). To choose another:
213
+
214
+ ```bash
215
+ eddie rounds ch04.adoc # see what exists
216
+ eddie list ch04.adoc --round 1 # newest mapping in round 1
217
+ eddie list ch04.adoc --mapping beta-proofread # by id (newest round that has it)
218
+ eddie list ch04.adoc --mapping rev-1/acme # exact round/id
219
+ eddie list ch04.adoc --mapping "Acme" # by label or origin
220
+ ```
221
+
222
+ **Annotations** are named by remark number (`7`, `#7`), full id
223
+ (`p4-highlight-72-506`), or a unique id prefix.
224
+
225
+ ## Command reference
226
+
227
+ `eddie help` lists every command, and `eddie help <command>` (or
228
+ `eddie <command> --help`) prints its full usage. The equivalent VS Code
229
+ command is given for each.
230
+
231
+ ### Reviews and rounds
232
+
233
+ #### `open` — map an annotated PDF onto its source
234
+
235
+ ```text
236
+ eddie open <source.adoc> <annotated.pdf>
237
+ [--new-round | --round N] [--label L] [--origin O] [--reviewer R]
238
+ [--type copyedit|proofread|developmental|technical|legal|other]
239
+ [--role annotated|proof|clean|stamped|other] [--import] [--no-anchor]
240
+ ```
241
+
242
+ Reads every mark, matches it to the source, writes the mapping, and anchors the
243
+ marks (unless `--no-anchor`). By default the PDF joins the document's current
244
+ round. `--new-round` starts the next round and carries state forward; `--round N`
245
+ targets a specific round. Opening the same PDF again in the same round updates
246
+ that mapping instead of creating a second one. `--import` copies the PDF into the
247
+ round's `pdf/` folder, so the round stays readable after your downloads folder is
248
+ cleared. If fewer than 40% of five or more marks are placed, a warning suggests
249
+ checking that the PDF belongs to this source. *VS Code: Open PDF Review, Start
250
+ New Review Round, Add Annotated PDF to Current Round.*
251
+
252
+ ```bash
253
+ eddie open ch04.adoc ch04-acme.pdf --origin Acme --reviewer "J. Smith" --type copyedit
254
+ eddie open ch04.adoc ch04-beta.pdf # second editor, same round
255
+ eddie open ch04.adoc ch04-acme-v2.pdf --new-round # the next draft came back
256
+ ```
257
+
258
+ #### `append` — add more PDFs to an existing mapping
259
+
260
+ ```text
261
+ eddie append <source.adoc> <file.pdf>... [--mapping M] [--origin O] [--reviewer R] [--role R] [--import]
262
+ ```
263
+
264
+ New marks join the mapping and continue its numbering. Marks it already has are
265
+ skipped, and adding a byte-identical PDF a second time does nothing. *VS Code:
266
+ Add PDFs to This Mapping.*
267
+
268
+ #### `merge` — fold mappings into one
269
+
270
+ ```text
271
+ eddie merge <source.adoc> <mapping>... --into <mapping> [--yes]
272
+ ```
273
+
274
+ Moves the PDFs, marks, replies and resolutions of each listed mapping into the
275
+ `--into` mapping (default: the newest), then deletes their sidecars. The target
276
+ is written to disk before anything is deleted. Asks for confirmation; `--yes`
277
+ skips the question. *VS Code: Merge Mappings into This One.*
278
+
279
+ #### `rounds` (alias `mappings`) — list rounds and mappings
280
+
281
+ ```text
282
+ eddie rounds <source.adoc> [--json]
283
+ ```
284
+
285
+ Each round with its mappings, the counts per bucket, and each sidecar path. *VS
286
+ Code: Switch Round / Mapping.*
287
+
288
+ #### `describe` (alias `edit-round`) — label a mapping and its round
289
+
290
+ ```text
291
+ eddie describe <source.adoc> [--mapping M] [--label L] [--origin O] [--reviewer R]
292
+ [--type T] [--role R] [--round-label L] [--received YYYY-MM-DD] [--round-note N]
293
+ ```
294
+
295
+ An empty value clears a field (`--label ''`). Round fields apply to every
296
+ mapping in the round. *VS Code: Edit Round Details.*
297
+
298
+ #### `delete` (aliases `delete-mapping`, `rm`) — remove a mapping
299
+
300
+ ```text
301
+ eddie delete <source.adoc> --mapping M [--yes]
302
+ ```
303
+
304
+ Deletes the sidecar only. The source, PDFs and reports are left in place.
305
+ `--mapping` or `--round` is required. *VS Code: Remove This Mapping.*
306
+
307
+ #### `remap` (alias `refresh`) — re-match after editing the source
308
+
309
+ ```text
310
+ eddie remap <source.adoc> [--only-changed]
311
+ ```
312
+
313
+ Re-runs matching for every mapping of the document against the current text,
314
+ using the annotations already extracted (the PDF is not read again). Run it after
315
+ editing the source outside an Eddie-aware editor. Annotations whose text has
316
+ changed too much are flagged stale and stay at their last known position rather
317
+ than being moved to a guess. *VS Code: Re-map Annotations.*
318
+
319
+ ### Annotations
320
+
321
+ #### `list` (alias `ls`) — what the editor said, grouped
322
+
323
+ ```text
324
+ eddie list <source.adoc> [--mapping M] [--bucket open,review,unmatched,resolved]
325
+ [--kind highlight,strikeout,underline,comment,insert,replace,other]
326
+ [--page N] [--text T] [--hide-resolved] [--json]
327
+ ```
328
+
329
+ `--text` searches comments, marked text, authors and notes. `--kind delete` is
330
+ accepted for `strikeout`. Without `--json` the counts summary goes to stderr and
331
+ the groups to stdout. *VS Code: the PDF Annotations tree.*
332
+
333
+ #### `show` — one annotation in full
334
+
335
+ ```text
336
+ eddie show <source.adoc> <item> [--mapping M] [--context N] [--json]
337
+ ```
338
+
339
+ Prints the editor's comment, the marked text, the source lines it points at with
340
+ `N` lines of context (default 1), how it was matched, its anchor, and the reply
341
+ thread with reply ids. *VS Code: Reveal in Source and the comment thread.*
342
+
343
+ #### `resolve`, `reopen` (alias `unresolve`), `confirm`
344
+
345
+ ```text
346
+ eddie resolve <source.adoc> <item>... [--mapping M]
347
+ eddie reopen <source.adoc> <item>... [--mapping M]
348
+ eddie confirm <source.adoc> <item>... [--mapping M] [--all-located]
349
+ ```
350
+
351
+ `resolve` and `reopen` set the resolved state (running either twice does
352
+ nothing more). `confirm` vouches for a low-confidence or stale match, which moves
353
+ it out of *Needs review*. `--all-located` applies to every annotation that has a
354
+ place in the source. *VS Code: Toggle Resolved, Confirm Match.*
355
+
356
+ #### `relink` — link an annotation by hand
357
+
358
+ ```text
359
+ eddie relink <source.adoc> <item> <line> [--mapping M]
360
+ ```
361
+
362
+ `<line>` is 1-based, as editors number lines. A hand-made link counts as
363
+ confirmed and overrides automatic matching until you run `remap-item`. *VS Code:
364
+ Re-link to Current Cursor Line, Reselect Source Line.*
365
+
366
+ #### `remap-item` — re-match one annotation
367
+
368
+ ```text
369
+ eddie remap-item <source.adoc> <item> [--mapping M]
370
+ ```
371
+
372
+ Drops any manual link and matches again. If the annotation has an anchor that
373
+ still resolves, the anchor is used. Exits with 1 when no confident place is found.
374
+ *VS Code: Re-map This Annotation.*
375
+
376
+ #### `triage` — place the unmatched
377
+
378
+ ```text
379
+ eddie triage <source.adoc> [--mapping M] [--candidates N] [-i | --interactive] [--json]
380
+ ```
381
+
382
+ For each unmatched, unresolved annotation, prints a ranked shortlist of candidate
383
+ lines (5 by default). With `-i`, asks about each one in turn: a candidate number
384
+ to link it, `L<line>` for any line, `s` to skip, `q` to stop. Without `-i` it
385
+ only prints; link the annotations afterwards with `relink`. *VS Code: Triage
386
+ Unmatched Annotations.*
387
+
388
+ #### `reply` — the conversation under a mark
389
+
390
+ ```text
391
+ eddie reply <source.adoc> <item> <text...> # add; prints the new reply id
392
+ eddie reply <source.adoc> <item> - # add, reading the text from stdin
393
+ eddie reply <source.adoc> <item> --edit <reply-id> <text...>
394
+ eddie reply <source.adoc> <item> --delete <reply-id>
395
+ [--mapping M] [--author NAME]
396
+ ```
397
+
398
+ Replies are saved in the sidecar, included in reports, and written into stamped
399
+ PDFs as threaded replies under the editor's mark. The author is `--author`, else
400
+ the `authorName` setting, else `git config user.name`, else your login name. The
401
+ editor's own mark cannot be edited, because it came from the PDF. *VS Code:
402
+ Reply, Edit Reply, Delete Reply.*
403
+
404
+ #### `preview` — where in the PDF
405
+
406
+ ```text
407
+ eddie preview <source.adoc> <item> [--mapping M] [--open]
408
+ ```
409
+
410
+ Prints `path/to/file.pdf#page=N`. `--open` opens the PDF with the system viewer.
411
+ *VS Code: Preview in PDF.*
412
+
413
+ ### Editing the source
414
+
415
+ #### `apply` — apply every confident edit
416
+
417
+ ```text
418
+ eddie apply <source.adoc> [--mapping M] [--dry-run] [--yes] [--json]
419
+ ```
420
+
421
+ Applies, in one write: struck text is deleted; highlighted, underlined or
422
+ replacement marks whose note contains a suggestion (`Reword: 'trust or lineage'`,
423
+ `Replace with "graph"`) are replaced with it; insert marks get their text. Only
424
+ unresolved annotations that are placed precisely and at or above `highConfidence`
425
+ are touched. Applied annotations are marked resolved and positions are re-matched.
426
+ The plan is printed first. In a terminal you are asked to confirm; elsewhere
427
+ `--yes` is required, otherwise nothing is written and the exit code is 1.
428
+ Overlapping edits are refused; apply those one at a time with `fix`. *VS Code:
429
+ Apply All Confident Edits.*
430
+
431
+ #### `fix` — one annotation's quick fix
432
+
433
+ ```text
434
+ eddie fix <source.adoc> <item> # list available actions
435
+ eddie fix <source.adoc> <item> <action> [--text T] [--dry-run] [--keep-open]
436
+ ```
437
+
438
+ | Action | For | Does |
439
+ | --- | --- | --- |
440
+ | `delete-struck` | strikeouts | Deletes exactly the struck words and one adjacent space. |
441
+ | `delete-lines` | strikeouts | Deletes the whole source lines the mark covers. |
442
+ | `replace-marked` | strikeouts, highlights and underlines with a note | Replaces the marked words with `--text`, or the note's suggestion. |
443
+ | `insert-at-mark` | carets | Inserts `--text`, or the note's suggestion, at the caret. |
444
+ | `insert-note` | any mark with a comment | Adds the editor's note above the line as a `// ✎` AsciiDoc comment. |
445
+
446
+ Prints the change as `-`/`+` lines. Afterwards the annotation is resolved, except
447
+ with `insert-note` or `--keep-open`. *VS Code: the lightbulb Quick Fixes.*
448
+
449
+ #### `anchor` — pin marks to their paragraphs
450
+
451
+ ```text
452
+ eddie anchor <source.adoc> [--mapping M] [--dry-run]
453
+ eddie anchor <source.adoc> --review <sidecar.json> [--dry-run]
454
+ ```
455
+
456
+ Writes `// eddie:<id>` markers for every placed annotation of the mapping that
457
+ does not have one yet, then re-matches through them. The `--review` form works on
458
+ one sidecar file directly, without a review folder. *VS Code: Anchor Annotations
459
+ in Source.*
460
+
461
+ #### `strip` — remove the markers
462
+
463
+ ```text
464
+ eddie strip <source.adoc> [--dry-run]
465
+ ```
466
+
467
+ Removes every `// eddie:` line. Annotations then fall back to block ids,
468
+ fingerprints and text matching. Use it before you hand off a source file that
469
+ must not contain the markers. *VS Code: Remove Source Anchors.*
470
+
471
+ ### Outputs
472
+
473
+ #### `report` (alias `export`) — a Markdown report for the editor
474
+
475
+ ```text
476
+ eddie report <source.adoc> [--mapping M] [-o out.md | --stdout] [--no-resolved]
477
+ ```
478
+
479
+ Groups annotations as Open, Needs review, Unmatched and Resolved, with line
480
+ numbers, comments and your replies. It warns when the source or PDF has changed
481
+ since the mapping was made. Written to the round folder
482
+ (`rev-1/<mapping>.review.md`), or beside the source with
483
+ `reportOutput: besideSource`, or to `-o`. The sidecar records where it was
484
+ written. With several editors in one round, export one report per mapping. *VS
485
+ Code: Export Review Report.*
486
+
487
+ #### `stamp` — write the review into a fresh render
488
+
489
+ ```text
490
+ eddie stamp <fresh.pdf> <source.adoc> [--mapping M | --all-in-round] [-o out.pdf] [--no-resolved]
491
+ eddie stamp <fresh.pdf> --review <sidecar.json>... [-o out.pdf] [--no-resolved] [--fail-on-unstamped]
492
+ ```
493
+
494
+ Places every mark and reply as a real PDF annotation in a copy of the clean
495
+ render. Positions are worked out again from the current source text, never taken
496
+ from the old PDF's coordinates, so page breaks, font and theme changes do not
497
+ matter. Where the marked words were since rewritten, the mark falls back to the
498
+ line or paragraph and says so in its comment. A mark that cannot be placed at all
499
+ is listed on stderr rather than put somewhere approximate. `--all-in-round` puts
500
+ every mapping of the round onto one PDF. Resolved marks are drawn faintly, or
501
+ left out with `--no-resolved`.
502
+
503
+ By default the output goes to the round's `pdf/` folder (`stampOutput:
504
+ besidePdf` puts it next to the clean render as `<name>.reviewed.pdf`). The
505
+ `--review` form needs no workspace, takes any number of sidecars, writes next to
506
+ the clean render, and with `--fail-on-unstamped` exits with 1 when anything could
507
+ not be placed, which suits a build gate. Refuses to overwrite the clean render.
508
+ *VS Code: Stamp Reviewed PDF.*
509
+
510
+ #### `extract` — the marks, without a source
511
+
512
+ ```text
513
+ eddie extract <annotated.pdf> [-o out.adoc | --stdout] [--json]
514
+ ```
515
+
516
+ Writes every annotation, with the text around it, to a standalone AsciiDoc file
517
+ (`<name>.annotations.adoc`), or prints the raw extraction as JSON. Nothing is
518
+ matched and no review is created. *VS Code: Extract Annotations as AsciiDoc.*
519
+
520
+ #### `map` — inspect matching without saving
521
+
522
+ ```text
523
+ eddie map <annotated.pdf> <source.adoc> [--json] [--report] [--lexical]
524
+ ```
525
+
526
+ Extracts and matches, prints the result, and writes nothing. `eddie a.pdf b.adoc`
527
+ is accepted as a short form.
528
+
529
+ ### Workspace
530
+
531
+ | Command | Does |
532
+ | --- | --- |
533
+ | `eddie status` (alias `reviews`) | Every reviewed document in the workspace, with open counts, rounds and the newest mapping. *VS Code: Switch Review.* |
534
+ | `eddie folder [source.adoc] [--open]` | The review folder, or one document's folder in it. *VS Code: Open Review Folder.* |
535
+ | `eddie migrate [--dry-run] [--yes]` | Moves old-layout sidecars (beside each `.adoc`) into the review folder, rewriting their relative paths. *VS Code: Move Reviews into Review Folder.* |
536
+ | `eddie config [--json]` | The workspace root, each effective setting and where it came from. |
537
+ | `eddie version` | The version. It matches the extension and plugin release that writes the same format. |
538
+
539
+ ### Global options
540
+
541
+ | Option | Meaning |
542
+ | --- | --- |
543
+ | `--root DIR` | Workspace root. Default: the nearest folder above the current directory containing `.eddie.json`, `.vscode`, `.eddie` or `.git`. This is the folder VS Code would have open, so both put files in the same place. |
544
+ | `-m, --mapping M` | Which mapping (see above). |
545
+ | `-r, --round N` | Which round. |
546
+ | `--review-folder DIR` | Override `reviewFolder`. |
547
+ | `--threshold X` | Override `matchThreshold`. |
548
+ | `--set key=value` | Override any setting; can be repeated. |
549
+ | `--author NAME` | Name on replies. |
550
+ | `--json` | Machine-readable output on stdout. |
551
+ | `--dry-run` | Show what would change; write nothing. |
552
+ | `-y, --yes` | Do not ask before changing the manuscript or deleting. |
553
+ | `-q, --quiet` | Fewer notices on stderr. |
554
+ | `-h, --help` | Help. |
555
+
556
+ ## Configuration
557
+
558
+ The CLI reads the same settings as the editors, with the same names and
559
+ defaults, so a project configured for VS Code needs no further setup. From
560
+ lowest to highest priority:
561
+
562
+ 1. Built-in defaults.
563
+ 2. `.vscode/settings.json` in the workspace root: the `eddieDoc.*` keys. The
564
+ file can contain comments and trailing commas, as VS Code allows.
565
+ 3. `.eddie.json` in the workspace root, for projects without VS Code. Keys can be
566
+ written as `reviewFolder`, `review-folder` or `review_folder`.
567
+ 4. Environment variables `EDDIE_DOC_<SETTING>`, e.g.
568
+ `EDDIE_DOC_REVIEW_FOLDER=reviews`, `EDDIE_DOC_AUTO_ANCHOR=false`.
569
+ 5. Command-line flags: `--set key=value`, `--review-folder`, `--threshold`.
570
+
571
+ ```json
572
+ // .eddie.json
573
+ {
574
+ "reviewFolder": "reviews",
575
+ "authorName": "Volodymyr Pavlyshyn",
576
+ "highConfidence": 0.8,
577
+ "autoAnchor": true
578
+ }
579
+ ```
580
+
581
+ `eddie config` shows where each value came from.
582
+
583
+ | Setting | Default | Meaning |
584
+ | --- | --- | --- |
585
+ | `reviewFolder` | `.eddie` | Folder holding every review file. Relative to the workspace root; `""` puts sidecars beside each `.adoc`. |
586
+ | `importPdfs` | `false` | Copy each annotated PDF into its round's `pdf/` folder when mapped. |
587
+ | `stampOutput` | `reviewFolder` | Where `stamp` writes: `reviewFolder` or `besidePdf`. |
588
+ | `reportOutput` | `reviewFolder` | Where `report` writes: `reviewFolder` or `besideSource`. |
589
+ | `matchThreshold` | `0.5` | Minimum similarity (0–1) to link an annotation automatically; below it the annotation is unmatched. |
590
+ | `highConfidence` | `0.75` | Score at or above which a link counts as confident. Below it the annotation goes to *Needs review* and `apply` skips it. |
591
+ | `autoAnchor` | `true` | Write `// eddie:` markers when a PDF is mapped. |
592
+ | `showResolved` | `true` | `false` hides resolved annotations from `list`. |
593
+ | `lexicalFallback` | `true` | Use character-trigram matching for annotations the token matcher could not place. Runs locally. |
594
+ | `lexicalThreshold` | `0.6` | Minimum score for the lexical fallback. |
595
+ | `semanticFallback` | `false` | Use embedding similarity from a local [Ollama](https://ollama.com) server for annotations still unplaced. |
596
+ | `ollamaUrl` | `http://localhost:11434` | Ollama server for the semantic fallback. |
597
+ | `embedModel` | `embeddinggemma` | Embedding model (`ollama pull <model>` first). |
598
+ | `semanticThreshold` | `0.62` | Minimum cosine similarity for the semantic fallback. |
599
+ | `authorName` | *(git user.name)* | Name on your replies. |
600
+
601
+ `inlineMarkers` and `expandThreads` are read but only affect the editors.
602
+ Embeddings for the semantic fallback are cached in
603
+ `$XDG_CACHE_HOME/eddie-doc/embed-cache.json` (default `~/.cache/eddie-doc/`).
604
+
605
+ ## Scripting: JSON output and exit codes
606
+
607
+ Results go to **stdout** and progress, warnings and summaries go to **stderr**,
608
+ so the output of any command can be piped. With `--json`, stdout carries one
609
+ JSON document.
610
+
611
+ ```bash
612
+ # How many annotations are still open?
613
+ eddie list ch04.adoc --json | jq '[.items[] | select(.bucket != "resolved")] | length'
614
+
615
+ # The id of every unmatched remark.
616
+ eddie list ch04.adoc --bucket unmatched --json | jq -r '.items[].number'
617
+
618
+ # Accept the top triage suggestion for every unmatched remark scoring 0.7 or more.
619
+ eddie triage ch04.adoc --json \
620
+ | jq -r '.[] | select(.candidates[0].score >= 0.7) | "\(.number) \(.candidates[0].line)"' \
621
+ | while read n line; do eddie relink ch04.adoc "$n" "$line"; done
622
+ ```
623
+
624
+ An annotation in `--json` output (from `list`, `show`, `relink`, `remap-item`):
625
+
626
+ ```json
627
+ {
628
+ "id": "p4-highlight-72-506",
629
+ "number": 4,
630
+ "initials": "A",
631
+ "kind": "highlight",
632
+ "bucket": "open",
633
+ "page": 4,
634
+ "line": 30,
635
+ "endLine": 31,
636
+ "score": 1,
637
+ "method": "marker",
638
+ "resolved": false,
639
+ "confirmed": false,
640
+ "stale": false,
641
+ "author": "Editor",
642
+ "comment": "Reword: 'trust or lineage'.",
643
+ "markedText": "confidence score or a provenance record",
644
+ "anchoredText": "…a confidence score or a provenance record…",
645
+ "anchor": "9b1f3c20",
646
+ "replies": [{ "id": "r-de401251", "author": "VP", "createdAt": "2026-10-13T09:12:44.000Z", "body": "Done." }]
647
+ }
648
+ ```
649
+
650
+ Line numbers are 1-based throughout. `null` means the annotation is unmatched.
651
+
652
+ | Exit code | Meaning |
653
+ | --- | --- |
654
+ | `0` | Success. |
655
+ | `1` | The command could not do what was asked: a file is missing, a document has no review, a confirmation was refused, nothing was placed by `remap-item`, or `stamp --fail-on-unstamped` left marks out. |
656
+ | `2` | Usage error: unknown command, missing argument, invalid flag value. |
657
+
658
+ The review files themselves follow a documented format with a JSON Schema:
659
+ [FORMAT.md](https://github.com/Volland/eddie-doc/blob/main/docs/FORMAT.md).
660
+
661
+ ## Recipes
662
+
663
+ ### Stamp every chapter after a build
664
+
665
+ ```bash
666
+ #!/usr/bin/env bash
667
+ set -euo pipefail
668
+ for src in manuscript/*.adoc; do
669
+ name=$(basename "$src" .adoc)
670
+ asciidoctor-pdf -o "build/$name.pdf" "$src"
671
+ # Only chapters that have a review.
672
+ if eddie rounds "$src" >/dev/null 2>&1; then
673
+ eddie stamp "build/$name.pdf" "$src" --all-in-round
674
+ fi
675
+ done
676
+ ```
677
+
678
+ ### Fail CI while remarks are open
679
+
680
+ ```yaml
681
+ # .github/workflows/review.yml
682
+ name: review
683
+ on: [pull_request]
684
+ jobs:
685
+ open-remarks:
686
+ runs-on: ubuntu-latest
687
+ steps:
688
+ - uses: actions/checkout@v4
689
+ - uses: actions/setup-node@v4
690
+ with: { node-version: 20 }
691
+ - run: npm install -g @eddie-doc/cli
692
+ - name: No open remarks on reviewed chapters
693
+ run: |
694
+ open=0
695
+ for src in manuscript/*.adoc; do
696
+ eddie rounds "$src" >/dev/null 2>&1 || continue
697
+ n=$(eddie list "$src" --json | jq '[.items[] | select(.bucket != "resolved")] | length')
698
+ echo "$src: $n open"
699
+ open=$((open + n))
700
+ done
701
+ test "$open" -eq 0
702
+ ```
703
+
704
+ ### A round from start to finish, without an editor
705
+
706
+ ```bash
707
+ eddie open ch04.adoc acme.pdf --new-round --origin Acme --type copyedit
708
+ eddie apply ch04.adoc --yes # take the mechanical edits
709
+ eddie triage ch04.adoc -i # place the rest
710
+ eddie list ch04.adoc --bucket open,review # what still needs a decision
711
+ eddie reply ch04.adoc 12 "Kept: the term is defined in chapter 2."
712
+ eddie resolve ch04.adoc 12
713
+ eddie report ch04.adoc
714
+ asciidoctor-pdf -o build/ch04.pdf ch04.adoc && eddie stamp build/ch04.pdf ch04.adoc
715
+ ```
716
+
717
+ ### With an AI coding agent
718
+
719
+ Every command can run without prompts (`--yes`) and produce JSON (`--json`), so
720
+ an agent can work through a review: list the open remarks, read each with
721
+ `eddie show`, edit the prose, then `eddie reply` and `eddie resolve`. The
722
+ remarks it handles show up as answered in VS Code and Obsidian, and in the
723
+ stamped PDF.
724
+
725
+ ```text
726
+ Work through the open remarks on manuscript/ch04.adoc with the `eddie` CLI:
727
+ `eddie list manuscript/ch04.adoc --bucket open,review --json`, then for each:
728
+ `eddie show … <n>`, make the edit, `eddie reply … <n> "<what you changed>"`,
729
+ `eddie resolve … <n>`. Leave remarks you disagree with open, with a reply
730
+ explaining why.
731
+ ```
732
+
733
+ ## Working with VS Code and Obsidian
734
+
735
+ - **Same files.** The CLI, the VS Code extension and the Obsidian plugin read and
736
+ write the same version 3 sidecars. Edit a review in any of them and the others
737
+ pick the changes up; VS Code and Obsidian merge changes made on disk while they
738
+ are open.
739
+ - **Same location.** The CLI finds the workspace root and reads
740
+ `.vscode/settings.json` the way VS Code does, so `.eddie/` ends up in the same
741
+ place. The Obsidian plugin's default folder is `Eddie Reviews`; to share a
742
+ vault, run the CLI with `--review-folder "Eddie Reviews"` or put it in
743
+ `.eddie.json`.
744
+ - **Bundled with the extension.** The VS Code extension includes this CLI:
745
+ - `eddie` is on the PATH of every integrated terminal (setting
746
+ `eddieDoc.cli.addToTerminalPath`, on by default).
747
+ - **Eddie Doc: Install 'eddie' Command in PATH** links it into `~/.local/bin`
748
+ (on Windows it shows the folder to add to PATH).
749
+ - **Eddie Doc: Open Terminal with Eddie CLI** opens a terminal in the workspace
750
+ and runs `eddie status`.
751
+
752
+ That copy runs on VS Code's own runtime, so it needs no Node install. Set
753
+ `EDDIE_NODE=/path/to/node` to run it on a different Node.
754
+ - **One version.** The npm package, the extension and the plugin are released
755
+ together under one version number. `eddie version` prints it, and each sidecar
756
+ records the version that wrote it.
757
+
758
+ ## Troubleshooting
759
+
760
+ **"no review for ch04.adoc".** Nothing has been mapped for this source yet, or
761
+ the review lives somewhere else. Check `eddie config` (is the root what you
762
+ expect, and is `reviewFolder` the one the editor uses?) and `eddie status`.
763
+
764
+ **Most annotations are unmatched.** The PDF is probably from a different source
765
+ file, or from a much older draft. Compare with `eddie map annotated.pdf ch04.adoc`.
766
+ Otherwise, use `eddie triage -i`, or lower `--threshold`, or enable
767
+ `semanticFallback` for paraphrased remarks.
768
+
769
+ **An annotation is stale.** The paragraph it was linked to changed too much. It
770
+ stays at its last known position in *Needs review*. Check it with `eddie show`,
771
+ then `eddie confirm` it or `eddie relink` it.
772
+
773
+ **`apply` refuses to run.** Without a terminal it needs `--yes`. If edits
774
+ overlap, apply them one at a time with `eddie fix`.
775
+
776
+ **Seeing pdf.js warnings.** They are hidden by default. Set `EDDIE_DEBUG=1` to
777
+ show them.
778
+
779
+ ## License
780
+
781
+ MIT. Part of [Eddie Doc](https://github.com/Volland/eddie-doc) by Volodymyr
782
+ Pavlyshyn.