@eddie-doc/cli 0.0.0-stage → 1.4.6

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