paper

command module
v0.0.0-...-d29b379 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: GPL-3.0 Imports: 21 Imported by: 0

README

paper

paper is a CLI tool for managing a local store of scientific papers with bibtex metadata. It provides a companion interface for Claude agents to organize, search, and manage research documents.

To install the latest version, run:

go install seehuhn.de/go/paper@latest

The store

The store is a plain directory, in a location of your choosing: one subdirectory per paper, named by its citation key, plus a config file, an inbox drop zone and an event log.

~/Papers/
├── config.json            contact email for the online services
├── inbox/                 drop zone for PDFs awaiting ingest
├── events/                local, best-effort log of command outcomes
└── <key>/                 one directory per paper
    ├── paper.json             single source of truth (see schema below)
    ├── published.pdf          version of record, when held
    ├── arxiv-2412.05039v2.pdf arXiv PDF, when held (version-qualified name)
    └── arxiv-2412.05039v2/    extracted arXiv tex source

<key> is surname_year of the first author (lowercase ASCII, disambiguated hoeffding_1963a/hoeffding_1963b when needed) and is simultaneously the directory name and the bibtex key. paper check enforces that the two match.

Everything is plain files: no database, no derived index. The store must stay navigable by a human without tooling.

config.json

Store-wide settings, all optional; a missing file means an empty configuration.

{"email": "you@example.com"}

email is the contact address the online metadata services are given. Unpaywall requires one and refuses to answer without it, and Crossref uses it to put requests in its faster, better-behaved "polite pool".

events/

One JSON line per command outcome, appended to events/<hostname>.jsonl: what was run, what it was given, and how it ended. The log is best-effort and local — a failure to write it never fails a command, and each machine writes only its own file, so a synced store never sees a conflict. No command reads it: it is kept so that what works in practice can be looked at later.

Store-root resolution

The store location is entirely user-configured; paper has no built-in default. Every command that touches the store resolves its root in this order:

  1. the -store flag, if given;
  2. the PAPER_STORE environment variable, if set.

If neither is set, the command fails with an error. The usual setup is to export PAPER_STORE in your shell profile, pointing at a directory that is synced between your machines (e.g. via syncthing).

Commands

Six commands are implemented so far (audit is planned but not yet built).

paper help

Lists the available commands. Also shown when paper is run with no arguments.

$ paper help
paper fetch [-dry-run] <ref>

Resolves a reference online and downloads what can be fetched reliably by script. The reference is a DOI, an arXiv ID or URL, or free text, which is put through a Crossref search and accepted only when one hit clearly wins. A DOI gets its metadata from Crossref and its PDF from Unpaywall, if there is an open-access route; an arXiv ID gets the preprint's PDF and tex source, with the published record merged in when the preprint names a DOI. -dry-run reports what would happen and writes nothing.

The failures are hand-offs to the calling agent, not bare errors. Where no download route exists the draft entry is still created, and the command exits nonzero reporting what it learned about the paper, what the entry holds, and where the file should go once found by hand. Free text that pins down no single paper creates no entry at all, and the error lists the candidates from Crossref, zbMATH and DBLP to choose between.

$ paper fetch 10.1080/01621459.1963.10500830
$ paper fetch arXiv:2412.05039
paper ingest [-since <ts>] [-into <key>] [-doi <doi>] [-arxiv <id>] <file.pdf>...

Works out which paper each PDF already on disk is, and moves it into the store. Identification is tiered: the Info dictionary first, then a DOI or arXiv stamp in the page text, then the title read off the first page and searched at Crossref. Nothing is downloaded — the file is already held, so the services are asked for metadata only. -since drops files older than a timestamp, and -doi/-arxiv say outright what a single file is, skipping identification.

-into is the hand-off from a failed fetch: once the agent has found the PDF by hand, it attaches that one file to the entry fetch left behind, after checking that the file really is that paper. A file that fails the check stays where it is.

$ paper ingest -since 2026-08-01T00:00:00 ~/Downloads/*.pdf
$ paper ingest -into hoeffding_1963 ~/Downloads/hoeffding.pdf
paper check [-online] [<key>...]

Validates entries: JSON parses, the entry type is known, required fields for that type are present, author/editor names parse, all fields transliterate cleanly, DOI/arXiv IDs are well-formed, and page ranges use --. With no arguments it also runs a store-wide fsck (key matches directory name, referenced files exist on disk, no stray sync-conflict files) and promotes any draft entry with zero errors to clean. Exits nonzero if any error-severity problem was found.

-online additionally looks every DOI up at Crossref. A DOI that does not resolve is an error — it is a typo or a hallucination. A title or year that disagrees, or a lookup that fails for any other reason, is only a warning: Crossref being unreachable or wrong must not condemn the entry.

$ paper check
$ paper check -online
paper search [-json] [-holdings <h>] [-status <s>] <terms>...

Ranked metadata search over every paper.json in the store (matches on key, author, title, year, journal/booktitle, DOI, arXiv ID, abstract). Draft entries and deprecated versions are flagged in the output. -json prints machine-readable results instead of one line per hit.

$ paper search -json voss
paper bib <key>... | -all

Deterministic bibtex generation from paper.json, sorted by key and separated by blank lines.

$ paper bib -all

Agent rule

Every store mutation goes through a paper command or ends with a passing paper check. Agents never free-form-manage the store: if paper cannot do something directly, edit paper.json by hand and then run paper check to validate the result.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal
bibtex
Package bibtex represents and parses BibTeX bibliography entries.
Package bibtex represents and parses BibTeX bibliography entries.
match
Package match provides utilities for matching and comparing strings, particularly for determining similarity between paper titles.
Package match provides utilities for matching and comparing strings, particularly for determining similarity between paper titles.
pdfid
Package pdfid extracts identifying information from PDF files: the document information dictionary and the text of the first few pages.
Package pdfid extracts identifying information from PDF files: the document information dictionary and the text of the first few pages.
pdfid/pdfidtest
Package pdfidtest generates small test PDFs for identification tests.
Package pdfidtest generates small test PDFs for identification tests.
resolve
Package resolve maps already-fetched source records (Crossref works, arXiv entries) onto draft store.Paper entries with bibtex-encoded fields.
Package resolve maps already-fetched source records (Crossref works, arXiv entries) onto draft store.Paper entries with bibtex-encoded fields.
store
Package store manages the on-disk store of papers: one directory per paper under a store root, each holding a paper.json that is the single source of truth for that paper.
Package store manages the on-disk store of papers: one directory per paper under a store root, each holding a paper.json that is the single source of truth for that paper.
tex
Package tex implements a one-way transliteration of bibtex-style, LaTeX-encoded strings (e.g.
Package tex implements a one-way transliteration of bibtex-style, LaTeX-encoded strings (e.g.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL