zotlit is a Zotero CLI for AI agents.
It focuses on a small set of tasks:
- add a Zotero item and return its
itemKey - search Semantic Scholar papers and import one into Zotero
- index local Zotero PDFs
- search indexed PDFs
- search bibliography metadata
- read or expand local passages by
itemKeyor file
addCreate a Zotero item from DOI metadata or basic fields.s2Search Semantic Scholar and pass a paperId intoadd.syncBuild or refresh the local PDF index.searchSearch indexed PDFs with default no-rerank hybrid search, optional--rerank, or--exactlexical search.metadataSearch bibliography metadata without runningsync.read/expandRead blocks from local manifests and expand around a hit.
Current scope:
- PDF only
- local indexing and search
- Zotero Web API writes for item creation
- Node.js
22+ - JDK
11+
Notes:
syncuses Java during PDF extractionsyncskips PDFs that time out or fail extraction, records them aserror, and continues the rest of the batchsyncskips unchanged extraction errors on later runs; use--retry-errorsto force another attemptsyncextracts books, chapters,/Book/attachments, and large PDFs one at a time instead of batching them with other PDFssyncwrites a readable log file todataDir/logs/and refreshesdataDir/logs/sync-latest.log- qmd may prepare local models on first use
From source:
npm install
npm run check
npm run build
node dist/cli.js helpDefault config file:
~/.zotlit/config.json
Minimal example:
{
"bibliographyJsonPath": "~/Library/CloudStorage/Dropbox/bibliography/bibliography.json",
"attachmentsRoot": "~/Library/Mobile Documents/com~apple~CloudDocs/Zotero",
"dataDir": "~/Library/Mobile Documents/com~apple~CloudDocs/Zotlit",
"semanticScholarApiKey": "<api-key>",
"zoteroLibraryId": "<library-id>",
"zoteroLibraryType": "user",
"zoteroCollectionKey": "<optional-collection-key>",
"zoteroApiKey": "<api-key>"
}API credentials can also come from environment variables:
ZOTLIT_SEMANTIC_SCHOLAR_API_KEYZOTLIT_ZOTERO_LIBRARY_IDZOTLIT_ZOTERO_LIBRARY_TYPEZOTLIT_ZOTERO_COLLECTION_KEYZOTLIT_ZOTERO_API_KEY
Fallback environment variable names:
SEMANTIC_SCHOLAR_API_KEYZOTERO_LIBRARY_IDZOTERO_LIBRARY_TYPEZOTERO_COLLECTION_KEYZOTERO_API_KEY
semanticScholarApiKey is only needed for zotlit s2 and zotlit add --s2-paper-id.
zoteroCollectionKey is optional and sets the default collection for new items created by add.
zoteroLibraryType supports both user and group.
sync treats bibliography attachment paths as relocatable under attachmentsRoot, so a bibliography exported on another machine still matches local PDFs when the relative path under the Zotero root is the same. Catalog paths under the local home directory are stored as ~/..., so an iCloud-backed dataDir can be shared across Macs with different usernames.
zotlit sync [--attachments-root <path>] [--retry-errors] [--pdf-timeout-ms <n>] [--pdf-batch-size <n>]
zotlit status
zotlit version
zotlit add [--doi <doi> | --s2-paper-id <id>] [--title <text>] [--author <name>] [--year <text>] [--publication <text>] [--url <url>] [--url-date <date>] [--collection-key <key>] [--item-type <type>]
zotlit s2 "<text>" [--limit <n>]
zotlit search "<text>" [--exact] [--limit <n>] [--min-score <n>] [--rerank]
zotlit metadata "<text>" [--limit <n>] [--field <field>] [--has-pdf]
zotlit read (--file <path> | --item-key <key>) [--offset-block <n>] [--limit-blocks <n>]
zotlit expand --file <path> --block-start <n> [--block-end <n>] [--radius <n>]Add by DOI:
zotlit add --doi "10.1111/dech.70058"Search Semantic Scholar and import by paperId:
zotlit s2 "active aging in China" --limit 5
zotlit add --s2-paper-id "f2005ed06241e8aa6f55f7ed9279a56b92038128"Add by fields:
zotlit add \
--title "Working Paper Title" \
--author "Jane Doe" \
--year 2026 \
--collection-key "ABCD1234" \
--publication "Working Paper Series" \
--url "https://example.com/paper" \
--url-date "2026-04-02"Group library example:
{
"zoteroLibraryId": "<group-id>",
"zoteroLibraryType": "group",
"zoteroApiKey": "<api-key>"
}Build or refresh the local index:
zotlit syncRetry unchanged extraction errors:
zotlit sync --retry-errorsExtract every PDF one at a time:
zotlit sync --pdf-batch-size 1Give large PDFs a longer per-extraction timeout:
zotlit sync --pdf-timeout-ms 600000Search indexed PDFs:
zotlit search "state-owned enterprise governance"
zotlit search "dangwei shuji" --exact
zotlit search "how do party secretaries shape SOE governance" --rerankDefault search skips qmd reranking for lower latency. Use --rerank only for narrower queries when ranking quality matters more than speed.
Search metadata:
zotlit metadata "Development and Change" --field journalRead and expand:
zotlit read --item-key KG326EEI
zotlit expand --file "~/Library/.../paper.pdf" --block-start 10 --radius 2addreturnsitemKeyimmediately, so an agent can cite the item right away.add --s2-paper-idprefers DOI import when Semantic Scholar returns a DOI, and falls back to Semantic Scholar metadata when it does not.addwrites to the library root by default. SetzoteroCollectionKeyin config or pass--collection-key <key>to place new items in a collection.- New items created by
addreceive the tagAdded by AI Agent. - Creating an item in Zotero does not make it instantly searchable in local PDF search.
metadatadepends on your exported bibliography JSON, and PDF search depends onsync. journalArticleitems keeppublicationTitlebut do not writepublisher.
MIT. See LICENSE.