citations
Check that the passages you quote actually appear in the sources you cite, and keep a library of the ones that do.
Install
pip install citations
Quick start
citations init
citations verify --claims claims/
2,940 quotes
found 2,940
not found 0
warnings
213 short — the source may qualify this in the next clause
155 normalized — matched after ignoring punctuation and spacing
all found.
Commands
| Command | What it does |
|---|---|
citations init |
Create a library here |
citations verify |
Do the quotations resolve in their sources? |
citations resolve |
Backfill missing DOIs and arXiv ids |
citations build |
Rebuild records from bibliographies |
citations lint |
BibTeX correctness, via papis |
citations link |
Point pdfs/ at the papers' artifacts |
Verify output
Three results, and they are exhaustive:
| Result | Meaning |
|---|---|
found |
The passage is in the source |
not found |
The source was read and the passage is not in it |
unchecked |
The source could not be read, so no measurement was made |
Warnings are separate, because a passage can be found and still worth a second look. A quote
can be short enough that the next clause changes its meaning — "We trained 50" appears
verbatim in a paper whose sentence continues "...and 5 refits each for 12 layered".
unchecked is neither a pass nor a failure. Only not found fails; --strict also fails on
unchecked, for CI.
not found means read the source. A mirror-reversed scan or a two-column extraction produces
the same signal as a passage that was never there.
Where the library lives
$CITATIONS_HOME if set
./.citations/ walking up this project's own, the way git finds .git
the shared library if you made one with citations init --user
none of those it tells you to run citations init
Project-local by default, so running the tool inside a paper works on that paper and there is no hidden global state.
What a claim file looks like
One file per source, in the paper's claims/ directory. citations verify --claims claims
reads all of them.
source:
citation: schiffman2026 # the bibkey
local: reference/schiffman2026.pdf # what gets read
sha256: 3f9a… # which bytes were read
extract_cmd: pdftotext
claims:
orthogonal-cores:
statement: 'Cores meeting equivalent causal criteria sit at principal angles of 75-90 degrees.'
quotes:
- exact: 'and principal angles ranged'
section: 'body'
statement is yours; exact is theirs. The tool checks the second only, so a statement that
overreaches its quote is for review to catch — the command cannot.
Records are YAML
So git diff shows what changed. A binary store cannot show you that a year moved from 2021 to
2022 — a real discrepancy this found between two of one author's own papers.
Claude Code
plugin/ is a Claude Code plugin that tells Claude when to reach for the CLI.
/plugin marketplace add elliottower/citations
/plugin install citations@citations
MIT licensed. docs/ has the working practices this came out of.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file citations-0.1.0.tar.gz.
File metadata
- Download URL: citations-0.1.0.tar.gz
- Upload date:
- Size: 35.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6a43a085319bd384d92488909e1209a7cade1d6cf9054832f1d0f4303308540
|
|
| MD5 |
95d56ea8095000ad42cb924a89b526d5
|
|
| BLAKE2b-256 |
2a14dfb2432636cee335aaefb36402e1f8c55d75799e6b37f566be0e8b024b40
|
Provenance
The following attestation bundles were made for citations-0.1.0.tar.gz:
Publisher:
publish.yml on elliottower/citations
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
citations-0.1.0.tar.gz -
Subject digest:
c6a43a085319bd384d92488909e1209a7cade1d6cf9054832f1d0f4303308540 - Sigstore transparency entry: 2500322012
- Sigstore integration time:
-
Permalink:
elliottower/citations@fc08b280ecef9e448f595ad2a1d1b816df16b00c -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/elliottower
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fc08b280ecef9e448f595ad2a1d1b816df16b00c -
Trigger Event:
release
-
Statement type:
File details
Details for the file citations-0.1.0-py3-none-any.whl.
File metadata
- Download URL: citations-0.1.0-py3-none-any.whl
- Upload date:
- Size: 26.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e8d21931219411129e9dcf8be619774fdb78e95815f06517763c1bae21dda38a
|
|
| MD5 |
5caf31f5ce0a40c11dc6d8f0302150ea
|
|
| BLAKE2b-256 |
27a9f6dd1f21994f37876a7808d947f408cf5720e8ba50b342ca6440ae311c91
|
Provenance
The following attestation bundles were made for citations-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on elliottower/citations
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
citations-0.1.0-py3-none-any.whl -
Subject digest:
e8d21931219411129e9dcf8be619774fdb78e95815f06517763c1bae21dda38a - Sigstore transparency entry: 2500322017
- Sigstore integration time:
-
Permalink:
elliottower/citations@fc08b280ecef9e448f595ad2a1d1b816df16b00c -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/elliottower
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fc08b280ecef9e448f595ad2a1d1b816df16b00c -
Trigger Event:
release
-
Statement type: