Tutorial
Round-trip with ReadCube Papers
Read in Papers, keep the library as the authority. You'll open a PDF in the app, export a batch Papers can import with your note and tags attached, import it yourself, and bring your highlights back. Commands used: /ref:note, /ref:open, /ref:export-papers, and /ref:pull-annotations. The last step covers what /ref:fetch does and doesn't do with Papers.
The journey
actions_queue is a sync queue that writes could corrupt. For the field-by-field mapping, see the Papers handoff section of the paper directory anatomy figure.The example uses project tdcs-review, batch wm-batch, and placeholder PMIDs 38000101 and 38000102, both screened included. Outputs labelled scratch library are real. There, --papers-db pointed at a synthetic SQLite file with Papers' items(id, collection_id, json) shape, never at the real library.
Steps
-
Write the note you want to carry over
notes.mdis your content, and/ref:noteis the only command that writes it. Each entry is appended with a timestamp.output from a scratch library
› /ref:note 38000101 "Strongest RCT so far; sham blinding reported in supplement." appended to …/lib/papers/38000101/notes.md
-
Open one PDF in Papers
/ref:openhands the acquired PDF to the OS withopen -a Papers, the same as double-clicking it in Finder. It's the only command that touches the app itself. With no PDF on file, it says so without failing:output from a scratch library
› /ref:open 38000101 { "pmid": "38000101", "status": "no_pdf_acquired", "message": "no acquired PDF for this paper yet — run /ref:fetch or /ref:attach first" }
Once
/ref:fetchor/ref:attachhas stored a PDF, the result becomes{"pmid": …, "status": "opened", "path": "…/raw/<sha256>/source.pdf"}. -
Plan the export with
--dry-runChoose the papers with any selector.
--dry-runresolves the set, checks for duplicates against a read-only snapshot of Papers, and prints the whole plan without writing a file. Claude looks for~/Library/Application Support/Papers/*.dband passes it as--papers-db.output from a scratch library (trimmed)
› /ref:export-papers --project tdcs-review --screened included --batch wm-batch --tags-from tdcs-review --notes --dry-run { "dry_run": true, "destination": "…/lib/exports/papers/wm-batch", "manifest": { "selector_expression": "--project tdcs-review --screened included", "pmids": ["38000101", "38000102"], "citekeys": ["rivera2023transcranial", "lindqvist2024working"], "destination_reason": "no destination configured; using exports/papers/<batch>/", "layout": "flat", "pdfs_mode": "copy", "per_paper": { "38000101": { "citekey": "rivera2023transcranial", "pdf_path": null, "already_known": false, "pushed_note_hash": "a52d955f…" }, "38000102": { "citekey": "lindqvist2024working", "pdf_path": null, "already_known": true, "pushed_note_hash": null } }, "missing_pdfs": ["38000101", "38000102"], "already_known": ["38000102"], "skipped_known": [], "field_omissions": { "38000101": ["volume", "issue", "pages", "issn"], … }, "foreign_conflicts": [], "duplicate_detection_available": true, … }, "note_statuses": { "38000101": "pushed", "38000102": "no notes.md content" } }
Option What it decides --to <dir> Destination. Otherwise the default is papers_export_dirfromconfig.json, and failing thatexports/papers/<batch>/, which the output reports asdestination_reason.--layout papers|flat papersmirrors the app's own<LastAuthor>/<Journal>-<Year>.pdfand adds-2,-3on collision. It's the default when writing into the configured folder.flatwrites<citekey>.pdfand is the default otherwise.--pdfs copy|link|none Copies are the default and leave raw/untouched.noneproduces a metadata-only bibliography with nolocal-url.--notes · --tags · --tags-from Put notes.mdinto the BibTeXnotefield and tags intokeywords.--tags-from <project>tags members with the project slug.--skip-known · --force Leave out papers the Papers snapshot already holds (matched on PMID, else DOI), or export them anyway. --collection <name> Only recorded in the manifest. It never creates a collection in Papers. --refresh Re-resolve the selector even if a manifest already exists at the destination. -
Export for real
Drop
--dry-run. This run also adds--skip-known, because38000102is already in Papers.output from a scratch library (trimmed)
› /ref:export-papers --project tdcs-review --screened included --batch wm-batch --tags-from tdcs-review --notes --skip-known { "dry_run": false, "destination": "…/lib/exports/papers/wm-batch", "manifest": { "pmids": ["38000101"], … "already_known": ["38000102"], "skipped_known": ["38000102"], … }, "note_statuses": { "38000101": "pushed" } }
This is the
references.bibit wrote, in Papers' own BibTeX dialect. There's nolocal-urlbecause this scratch paper has no PDF yet:@article{rivera2023transcranial, title = {{Transcranial stimulation and working memory in adults with ADHD: a randomized trial}}, abstract = {{We randomized 80 adults...}}, author = {Rivera, Ana and Okafor, Chidi and Lindqvist, Maja}, journal = {J Atten Disord}, year = 2023, doi = {10.1000/example.101}, pmid = {38000101}, pmcid = {PMC0000101}, note = {--- 2026-09-15T10:17:36.789825+00:00 Strongest RCT so far; sham blinding reported in supplement.}, keywords = {tdcs-review} }Foreign files are never overwritten. The export keeps its own record of every path it wrote (.ref-manager-written.json). A file at a target path that isn't in that record belongs to someone else, usually Papers. It's reported underforeign_conflictsand left alone.No Papers database? The export still runs. If the snapshot can't be taken, the manifest shows"duplicate_detection_available": falsewith the reason, for example"no database at …/missing.db", and the export carries on. -
Import in Papers (you)
In Papers, import
references.bibfrom the destination folder through the app's own import dialog. The plugin doesn't drive the app, and it doesn't assume a watched folder picks the file up. Once imported, check the Notes and Tags columns: the note and thetdcs-reviewtag should be there. -
Re-exporting won't overwrite a note you've edited in Papers
You may have edited the note inside Papers since the first export, so a second
--notespush is refused for any paper whose note has already been pushed. The entry is still exported, just without the note.output from a scratch library (trimmed)
› /ref:export-papers 38000101 --batch wm-batch-2 --notes { … "note_statuses": { "38000101": "refused: note already pushed for 38000101 (hash a52d955f5b90...); use --notes=force to overwrite" } }
The refusal is library-wide. Pushes are tracked per PMID inexports/papers/note_pushes.json, not in each destination's manifest. That's why the example above is refused even thoughwm-batch-2is a new destination.--notes=forceon the slash command maps to the script's--notes-forceflag and overwrites.Highlights never travel outbound. BibTeX has no field for them. They're also anchored to a file hash and page rectangles that only make sense against Papers' own copy of the PDF. Annotations move in one direction: from Papers into the library. -
Pull your annotations back
/ref:pull-annotationsreads the matching Papers item from a fresh snapshot and upsertsannotations.json, keyed by each annotation's own Papersid.notes.mdis never touched.output from a scratch library
› /ref:pull-annotations 38000102 { "pmid": "38000102", "status": "ok", "added": ["ann-1", "ann-2"], "updated": [], "tombstoned": [], "unchanged": [] } # again, with nothing changed upstream › /ref:pull-annotations 38000102 { "pmid": "38000102", "status": "ok", "added": [], "updated": [], "tombstoned": [], "unchanged": ["ann-1", "ann-2"] } # after deleting the margin note ann-2 in Papers › /ref:pull-annotations 38000102 { "pmid": "38000102", "status": "ok", "added": [], "updated": [], "tombstoned": ["ann-2"], "unchanged": ["ann-1"] }
The other statuses don't fail the command either:
output from a scratch library
# no Papers database found { "pmid": "38000102", "status": "no_papers_db_configured" } # this paper isn't in your Papers library { "pmid": "38000101", "status": "no_matching_papers_item" } # the live database couldn't be read: "status": "snapshot_unavailable", with a "reason"
Why tombstones. An annotation that disappears upstream might be an accidental delete or a sync hiccup. Locally it getsdeleted_upstream: trueand a timestamp, and it stays in the file. Thetombstonedlist makes the loss visible instead of silent. -
Full text from Papers: not implemented yet
PLAN.md§7 describes an opportunistic step: during/ref:fetch, reuse Papers' own extracted full text when the app already holds the paper. The implemented/ref:fetchdoesn't do this. It tries PMC OA, then Unpaywall, then publisher HTML, and otherwise records the paper as abstract-only. If a PDF is only available inside Papers, export it from the app and attach it:illustrative output
› /ref:attach 38000102 ~/Downloads/lindqvist-2024.pdf # attached / duplicate_noop / refused (DOI and title not found on the first two pages) / failed › /ref:open 38000102 # now "status": "opened"
What you now have
<LIBRARY>/
papers/38000101/
notes.md # yours; exported once as the BibTeX note
papers/38000102/
annotations.json # pulled from Papers; tombstones kept
exports/papers/
note_pushes.json # per-PMID hash of each pushed note, library-wide
wm-batch/
references.bib # Papers dialect: note, keywords, local-url when a PDF exists
manifest.json # selector, PMIDs, PDF paths, duplicates, omissions, conflicts
.ref-manager-written.json # paths this plugin owns at the destination
<citekey>.pdf # with --pdfs copy|link and an acquired PDF
wm-batch-2/ …
~/Library/Application Support/Papers/*.db # read through snapshots only, never written
Command reference: /ref:export-papers, /ref:open, /ref:pull-annotations, /ref:fetch.