zotcli 0.1.2__tar.gz → 0.2.1__tar.gz
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.
- {zotcli-0.1.2 → zotcli-0.2.1}/.gitignore +6 -0
- zotcli-0.2.1/CHANGELOG.md +112 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/PKG-INFO +180 -152
- zotcli-0.2.1/PLAN_WRITE.md +883 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/README.md +167 -150
- zotcli-0.2.1/SKILL.md +305 -0
- zotcli-0.2.1/docs/architecture-write.md +221 -0
- zotcli-0.2.1/docs/commands.md +514 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/pyproject.toml +6 -3
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/__init__.py +1 -1
- zotcli-0.2.1/src/zotcli/cli/add.py +2267 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/cli/collections.py +41 -0
- zotcli-0.2.1/src/zotcli/cli/config_cmd.py +55 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/cli/export.py +30 -20
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/cli/items.py +10 -1
- zotcli-0.2.1/src/zotcli/cli/main.py +142 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/cli/render.py +6 -1
- zotcli-0.2.1/src/zotcli/config.py +252 -0
- zotcli-0.2.1/src/zotcli/logging_setup.py +108 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/models.py +13 -1
- zotcli-0.2.1/src/zotcli/paths.py +100 -0
- zotcli-0.2.1/src/zotcli/write/__init__.py +5 -0
- zotcli-0.2.1/src/zotcli/write/browser.py +321 -0
- zotcli-0.2.1/src/zotcli/write/citation_pipeline.py +228 -0
- zotcli-0.2.1/src/zotcli/write/collection_assign.py +57 -0
- zotcli-0.2.1/src/zotcli/write/connector_client.py +529 -0
- zotcli-0.2.1/src/zotcli/write/credentials.py +162 -0
- zotcli-0.2.1/src/zotcli/write/csl_json.py +268 -0
- zotcli-0.2.1/src/zotcli/write/dedup.py +186 -0
- zotcli-0.2.1/src/zotcli/write/identifiers.py +225 -0
- zotcli-0.2.1/src/zotcli/write/pdf.py +190 -0
- zotcli-0.2.1/src/zotcli/write/preflight.py +81 -0
- zotcli-0.2.1/src/zotcli/write/recognize.py +92 -0
- zotcli-0.2.1/src/zotcli/write/resolvers/__init__.py +65 -0
- zotcli-0.2.1/src/zotcli/write/resolvers/arxiv.py +174 -0
- zotcli-0.2.1/src/zotcli/write/resolvers/crossref.py +237 -0
- zotcli-0.2.1/src/zotcli/write/resolvers/ieee.py +220 -0
- zotcli-0.2.1/src/zotcli/write/resolvers/openalex.py +275 -0
- zotcli-0.2.1/src/zotcli/write/resolvers/openlibrary.py +153 -0
- zotcli-0.2.1/src/zotcli/write/resolvers/pubmed.py +229 -0
- zotcli-0.2.1/src/zotcli/write/resolvers/sciencedirect.py +173 -0
- zotcli-0.2.1/src/zotcli/write/resolvers/semantic_scholar.py +135 -0
- zotcli-0.2.1/src/zotcli/write/resolvers/unpaywall.py +144 -0
- zotcli-0.2.1/src/zotcli/write/session.py +255 -0
- zotcli-0.2.1/tests/fixtures/csl/crossref_numpy.json +52 -0
- zotcli-0.2.1/tests/fixtures/sample.bib +11 -0
- zotcli-0.2.1/tests/fixtures/sample.epub +0 -0
- zotcli-0.2.1/tests/fixtures/sample.pdf +25 -0
- zotcli-0.2.1/tests/fixtures/sample.ris +14 -0
- zotcli-0.2.1/tests/integration/__init__.py +0 -0
- zotcli-0.2.1/tests/integration/test_add_cite.py +316 -0
- zotcli-0.2.1/tests/integration/test_add_file.py +340 -0
- zotcli-0.2.1/tests/integration/test_add_import.py +388 -0
- zotcli-0.2.1/tests/integration/test_add_pipeline.py +522 -0
- zotcli-0.2.1/tests/integration/test_add_url.py +343 -0
- zotcli-0.2.1/tests/integration/test_auto_detect.py +339 -0
- zotcli-0.2.1/tests/integration/test_batch.py +335 -0
- zotcli-0.2.1/tests/integration/test_connector_client.py +115 -0
- zotcli-0.2.1/tests/integration/test_verbose.py +210 -0
- zotcli-0.2.1/tests/integration/test_with_pdf.py +382 -0
- zotcli-0.2.1/tests/unit/__init__.py +0 -0
- zotcli-0.2.1/tests/unit/test_browser_optional_dep.py +139 -0
- zotcli-0.2.1/tests/unit/test_citation_pipeline.py +368 -0
- zotcli-0.2.1/tests/unit/test_config_write_section.py +148 -0
- zotcli-0.2.1/tests/unit/test_credentials.py +178 -0
- zotcli-0.2.1/tests/unit/test_csl_json.py +309 -0
- zotcli-0.2.1/tests/unit/test_dedup.py +360 -0
- zotcli-0.2.1/tests/unit/test_identifiers.py +304 -0
- zotcli-0.2.1/tests/unit/test_logging_setup.py +143 -0
- zotcli-0.2.1/tests/unit/test_paths.py +199 -0
- zotcli-0.2.1/tests/unit/test_pdf.py +262 -0
- zotcli-0.2.1/tests/unit/test_recognize.py +216 -0
- zotcli-0.2.1/tests/unit/test_resolvers/__init__.py +0 -0
- zotcli-0.2.1/tests/unit/test_resolvers/test_arxiv.py +136 -0
- zotcli-0.2.1/tests/unit/test_resolvers/test_crossref.py +103 -0
- zotcli-0.2.1/tests/unit/test_resolvers/test_crossref_search.py +135 -0
- zotcli-0.2.1/tests/unit/test_resolvers/test_ieee.py +217 -0
- zotcli-0.2.1/tests/unit/test_resolvers/test_openalex.py +171 -0
- zotcli-0.2.1/tests/unit/test_resolvers/test_openlibrary.py +96 -0
- zotcli-0.2.1/tests/unit/test_resolvers/test_pubmed.py +126 -0
- zotcli-0.2.1/tests/unit/test_resolvers/test_sciencedirect.py +174 -0
- zotcli-0.2.1/tests/unit/test_resolvers/test_semantic_scholar.py +169 -0
- zotcli-0.2.1/tests/unit/test_resolvers/test_unpaywall.py +235 -0
- zotcli-0.1.2/SKILL.md +0 -227
- zotcli-0.1.2/docs/commands.md +0 -182
- zotcli-0.1.2/src/zotcli/cli/main.py +0 -71
- zotcli-0.1.2/src/zotcli/config.py +0 -78
- {zotcli-0.1.2 → zotcli-0.2.1}/.github/workflows/docs.yml +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/.github/workflows/publish.yml +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/docs/api_reference.md +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/docs/cli_reference.md +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/docs/data_models.md +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/docs/getting_started.md +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/docs/index.md +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/mkdocs.yml +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/__main__.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/cli/__init__.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/cli/attachments.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/cli/search.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/cli/stats.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/db.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/export/__init__.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/export/bibtex.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/export/csv_.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/export/json_.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/export/markdown.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/queries/__init__.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/queries/attachments.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/queries/collections.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/queries/items.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/queries/search.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/src/zotcli/queries/tags.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/test_script.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/tests/__init__.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/tests/conftest.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/tests/test_cli.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/tests/test_db.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/tests/test_export.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/tests/test_queries.py +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/zot.png +0 -0
- {zotcli-0.1.2 → zotcli-0.2.1}/zotcli.code-workspace +0 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.2.0] — 2026-05-10
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`zot add` command group** — adds items to Zotero via its local connector HTTP server (port 23119). Zotero must be running; `zotero.sqlite` is never modified directly.
|
|
10
|
+
- **Smart auto-detect dispatcher** — bare `zot add "<anything>"` identifies DOI, arXiv ID, PMID, ISBN, IEEE/ScienceDirect URL, citation string, or local file path automatically.
|
|
11
|
+
- **`zot add doi`** — resolve via Crossref, send to Zotero. Supports `--collection`, `--tag`, `--dry-run`, `--with-pdf`, `--on-duplicate`.
|
|
12
|
+
- **`zot add arxiv`** — resolve via arXiv Atom feed → CSL-JSON.
|
|
13
|
+
- **`zot add pmid`** — resolve via NCBI eutils.
|
|
14
|
+
- **`zot add isbn`** — resolve via OpenLibrary (Google Books fallback).
|
|
15
|
+
- **`zot add cite`** — free-text citation string → Crossref bibliographic search → DOI; fallback OpenAlex then Semantic Scholar. Interactive disambiguation table when confidence is low. `--file` for batch citation files.
|
|
16
|
+
- **`zot add url`** — auto-routes arXiv/PubMed/IEEE/ScienceDirect/doi.org URLs to the relevant resolver; generic URLs fall back to `saveSnapshot`.
|
|
17
|
+
- **`zot add file`** — stream a local PDF or EPUB to `/connector/saveStandaloneAttachment`; polls for Zotero's `RecognizeDocument` result (configurable timeout, default 30 s).
|
|
18
|
+
- **`zot add import`** — POST raw RIS / BibTeX / CSL-JSON bytes to `/connector/import`.
|
|
19
|
+
- **`zot add batch`** — process a file of mixed inputs (one per line); summary table at end; non-zero exit if any line failed.
|
|
20
|
+
- **`zot add login`** — manage service credentials: Unpaywall (email), IEEE Xplore (browser SSO), ScienceDirect (browser SSO). `--reset` to clear. `--install-browser` for first-time Playwright Chromium install.
|
|
21
|
+
- **`--with-pdf` flag** on doi/arxiv/pmid/isbn/cite/url — attach an open-access PDF using Unpaywall; fall back to publisher cookies (IEEE/Elsevier) if browser-authenticated.
|
|
22
|
+
- **`zot add status`** — preflight check: reports Zotero reachability, selected collection, and connector URL.
|
|
23
|
+
- **`zot config` command group** — `get`, `set`, `path` subcommands for reading and writing `<zotcli-home>/config.toml`.
|
|
24
|
+
- **Self-contained data directory** (`<zotcli-home>`) — resolved via `ZOTCLI_HOME` env → SKILL.md sibling search → `~/.zotcli`. Holds `config.toml`, `credentials.json`, `cookies/`, `cache/`, `logs/`.
|
|
25
|
+
- **Write gate** — `write.enabled = false` by default. Enable once with `zot config set write.enabled true`, or pass `--allow-write` / set `ZOTCLI_ALLOW_WRITE=1` per command.
|
|
26
|
+
- **Duplicate detection** (report-only by default) — on duplicate DOI/arXiv, prints existing item key + title and exits 0; no mutation.
|
|
27
|
+
- **`--dry-run`** on all add subcommands — resolves metadata and prints the payload that would be sent, without contacting the connector.
|
|
28
|
+
- **`-v` / `--verbose`** — echoes every HTTP request and response to stderr.
|
|
29
|
+
- **`--non-interactive`** — suppresses all prompts; used in scripts and agent contexts.
|
|
30
|
+
- **Rotating log** at `<zotcli-home>/logs/zot.log` (1 MB × 3 backups).
|
|
31
|
+
- **Optional dependency groups**: `write = ["httpx>=0.27"]`, `browser = ["playwright>=1.40"]`, `all` aggregates all extras.
|
|
32
|
+
- **441 unit + integration tests** (up from 40 in v0.1.3). e2e tests are opt-in (`pytest -m e2e`).
|
|
33
|
+
- **`docs/architecture-write.md`** — maintainer-facing overview of the write-path design.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- `README.md` and `SKILL.md` updated to document write capability and correct the "never writes" framing. The accurate framing is: default is strictly read-only; writes require opt-in and go through Zotero's connector.
|
|
38
|
+
- `pyproject.toml` description updated; version bumped to 0.2.0.
|
|
39
|
+
- `docs/commands.md` extended with full `zot add` and `zot config` reference.
|
|
40
|
+
- SKILL.md `description:` frontmatter now advertises both reading and writing.
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- Click 8.2 `mix_stderr` keyword removal (M5 fix).
|
|
45
|
+
- `RotatingFileHandler` lock-pairing bug in logging setup (M5 fix).
|
|
46
|
+
- `updateSession` target correctly sent as flat string, not nested object (M2 fix).
|
|
47
|
+
|
|
48
|
+
### Deferred to v0.3
|
|
49
|
+
|
|
50
|
+
- Edit / delete existing items — blocked on Zotero local API not yet supporting writes, or requires shipping a `.xpi` plugin. Tracked in `PLAN_WRITE.md §12`.
|
|
51
|
+
- Parallel connector calls in `zot add batch` (`--jobs N` accepted but no-op; sequential connector calls are safe; parallel resolver lookups deferred).
|
|
52
|
+
- Group library targeting beyond current-selection default.
|
|
53
|
+
|
|
54
|
+
### Manual smoke test
|
|
55
|
+
|
|
56
|
+
Run the following commands against a live Zotero instance before tagging v0.2.0:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Sanity
|
|
60
|
+
zot --version
|
|
61
|
+
zot stats
|
|
62
|
+
|
|
63
|
+
# Write gate
|
|
64
|
+
zot config set write.enabled true
|
|
65
|
+
zot config get write.enabled # should print "true"
|
|
66
|
+
zot config path # should print <zotcli-home>
|
|
67
|
+
|
|
68
|
+
# Preflight
|
|
69
|
+
zot add status # should report Zotero as reachable
|
|
70
|
+
|
|
71
|
+
# Add by identifier
|
|
72
|
+
zot add doi 10.1038/s41586-020-2649-2 --collection Inbox --dry-run
|
|
73
|
+
zot add doi 10.1038/s41586-020-2649-2 --collection Inbox --tag smoke-test
|
|
74
|
+
zot add arxiv 2401.12345 --collection Preprints
|
|
75
|
+
zot add pmid 31452104
|
|
76
|
+
zot add isbn 978-0-262-03384-8 --collection Books
|
|
77
|
+
|
|
78
|
+
# Add by URL
|
|
79
|
+
zot add url https://ieeexplore.ieee.org/document/9876543
|
|
80
|
+
zot add url https://www.sciencedirect.com/science/article/pii/S2352467725000102
|
|
81
|
+
|
|
82
|
+
# Free-text citation
|
|
83
|
+
zot add cite "Zhang, J., Geth, F., Heidari, R., Verbič, G. (2025) Beyond simplifications: Evaluating assumptions for low-voltage network modelling in the DER era."
|
|
84
|
+
|
|
85
|
+
# Local file
|
|
86
|
+
zot add file ./paper.pdf --collection Inbox
|
|
87
|
+
|
|
88
|
+
# Import
|
|
89
|
+
zot add import refs.bib --collection "Imports/test"
|
|
90
|
+
|
|
91
|
+
# Batch
|
|
92
|
+
printf '10.1109/TPWRS.2023.1234567\n2401.12345\n' > /tmp/papers.txt
|
|
93
|
+
zot add batch /tmp/papers.txt --collection "Batch Test"
|
|
94
|
+
|
|
95
|
+
# Paywalled PDF (optional: requires prior login)
|
|
96
|
+
zot add login --service unpaywall
|
|
97
|
+
zot add doi 10.1038/s41586-020-2649-2 --with-pdf
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## [0.1.3] — 2026-04-13
|
|
103
|
+
|
|
104
|
+
### Added
|
|
105
|
+
|
|
106
|
+
- Item export command (`zot export`) with JSON, CSV, BibTeX, and Markdown formats.
|
|
107
|
+
- Collection display improvements and recursive collection traversal.
|
|
108
|
+
|
|
109
|
+
### Fixed
|
|
110
|
+
|
|
111
|
+
- Removed unsupported schema version warnings from schema validation.
|
|
112
|
+
- Database path auto-detection clarified for Linux, macOS, and Windows.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zotcli
|
|
3
|
-
Version: 0.1
|
|
4
|
-
Summary: A
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: A CLI for browsing, exporting, and adding items to a Zotero library
|
|
5
5
|
Author-email: MohamedNumair <mo7amednumair@gmail.com>
|
|
6
6
|
License: MIT
|
|
7
7
|
Requires-Python: >=3.10
|
|
@@ -11,18 +11,29 @@ Requires-Dist: pydantic>=2.0
|
|
|
11
11
|
Requires-Dist: python-dateutil>=2.9
|
|
12
12
|
Requires-Dist: rich>=13.0
|
|
13
13
|
Provides-Extra: all
|
|
14
|
+
Requires-Dist: httpx>=0.27; extra == 'all'
|
|
14
15
|
Requires-Dist: jinja2>=3.1; extra == 'all'
|
|
16
|
+
Requires-Dist: playwright>=1.40; extra == 'all'
|
|
15
17
|
Requires-Dist: pybtex>=0.24; extra == 'all'
|
|
16
18
|
Provides-Extra: bibtex
|
|
17
19
|
Requires-Dist: pybtex>=0.24; extra == 'bibtex'
|
|
20
|
+
Provides-Extra: browser
|
|
21
|
+
Requires-Dist: playwright>=1.40; extra == 'browser'
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest-httpserver; extra == 'dev'
|
|
25
|
+
Requires-Dist: tomli-w; extra == 'dev'
|
|
26
|
+
Requires-Dist: vcrpy; extra == 'dev'
|
|
18
27
|
Provides-Extra: export
|
|
19
28
|
Requires-Dist: jinja2>=3.1; extra == 'export'
|
|
29
|
+
Provides-Extra: write
|
|
30
|
+
Requires-Dist: httpx>=0.27; extra == 'write'
|
|
20
31
|
Description-Content-Type: text/markdown
|
|
21
32
|
|
|
22
33
|
# zotcli (`zot`)
|
|
23
34
|

|
|
24
35
|
|
|
25
|
-
A
|
|
36
|
+
A command-line interface for your local [Zotero](https://www.zotero.org/) library. **Default: strictly read-only.** Write capabilities are opt-in and route through Zotero's own connector HTTP server — `zotero.sqlite` is **never** modified directly.
|
|
26
37
|
|
|
27
38
|
**Library stats on this machine:** 3,771 items · 200 collections · 3,201 tags · 6.2 GB storage
|
|
28
39
|
|
|
@@ -31,7 +42,17 @@ A "crazy" good read-only command-line interface for your local [Zotero](https://
|
|
|
31
42
|
## Installation
|
|
32
43
|
|
|
33
44
|
```bash
|
|
45
|
+
# Read-only (default)
|
|
34
46
|
pip install zotcli
|
|
47
|
+
|
|
48
|
+
# Adds the write API client (httpx) — required for any zot add … command
|
|
49
|
+
pip install "zotcli[write]"
|
|
50
|
+
|
|
51
|
+
# Adds Playwright for paywalled-PDF retrieval via browser SSO
|
|
52
|
+
pip install "zotcli[browser]"
|
|
53
|
+
|
|
54
|
+
# Everything
|
|
55
|
+
pip install "zotcli[all]"
|
|
35
56
|
```
|
|
36
57
|
|
|
37
58
|
Verify:
|
|
@@ -41,7 +62,7 @@ zot --help
|
|
|
41
62
|
|
|
42
63
|
The database at `~/Zotero/zotero.sqlite` is auto-detected on WSL. Override anytime with `--db PATH`.
|
|
43
64
|
|
|
44
|
-
|
|
65
|
+
Depending on the operating system the Zotero folder can be in different locations. By default, zotcli looks for the database at `~/Zotero/zotero.sqlite`, which works for most setups. For Windows users, the Zotero folder is typically located in `C:\Users\<YourUsername>\Zotero`. For macOS users, it is usually found in `~/Zotero`. If you have a custom setup or want to specify a different path, you can use the `--db` option to point zotcli to the correct location of your `zotero.sqlite` file.
|
|
45
66
|
|
|
46
67
|
---
|
|
47
68
|
|
|
@@ -50,24 +71,47 @@ depending on the operating system the Zotero folder can be in different location
|
|
|
50
71
|
```
|
|
51
72
|
zotcli/
|
|
52
73
|
├── PLAN.md # Original design document
|
|
74
|
+
├── PLAN_WRITE.md # Write-capability design document
|
|
53
75
|
├── SKILL.md # Agent skill descriptor
|
|
54
76
|
├── pyproject.toml # Package metadata and dependencies
|
|
55
77
|
├── docs/
|
|
56
|
-
│
|
|
78
|
+
│ ├── commands.md # Full command reference
|
|
79
|
+
│ └── architecture-write.md # Write-path architecture overview
|
|
57
80
|
├── src/
|
|
58
81
|
│ └── zotcli/
|
|
59
82
|
│ ├── __init__.py
|
|
60
83
|
│ ├── __main__.py # python -m zotcli entrypoint
|
|
61
84
|
│ ├── db.py # Read-only SQLite connection + auto-discovery
|
|
62
|
-
│ ├── config.py # TOML config
|
|
85
|
+
│ ├── config.py # TOML config + [write]/[unpaywall]/[browser] sections
|
|
86
|
+
│ ├── paths.py # Cross-platform self-contained path resolution
|
|
63
87
|
│ ├── models.py # Pydantic v2 models: Item, Collection, Creator, Attachment, Note
|
|
64
88
|
│ ├── queries/
|
|
65
|
-
│ │ ├── items.py # Core item fetch
|
|
66
|
-
│ │ │ # collections, attachments, notes in one go)
|
|
89
|
+
│ │ ├── items.py # Core item fetch
|
|
67
90
|
│ │ ├── collections.py # Collection tree queries
|
|
68
91
|
│ │ ├── attachments.py # Attachment path resolution helpers
|
|
69
92
|
│ │ ├── tags.py # Tag queries
|
|
70
|
-
│ │ └── search.py
|
|
93
|
+
│ │ └── search.py # Field search, author search, DOI, year, fulltext
|
|
94
|
+
│ ├── write/ # Write-path package (requires zotcli[write])
|
|
95
|
+
│ │ ├── connector_client.py # httpx client for /connector/*
|
|
96
|
+
│ │ ├── preflight.py # Zotero liveness check
|
|
97
|
+
│ │ ├── session.py # Session lifecycle + updateSession
|
|
98
|
+
│ │ ├── csl_json.py # CSL-JSON ↔ connector item shape
|
|
99
|
+
│ │ ├── identifiers.py # detect_kind: DOI / arXiv / PMID / ISBN / URL / citation
|
|
100
|
+
│ │ ├── dedup.py # Read-only duplicate check against DB
|
|
101
|
+
│ │ ├── citation_pipeline.py # Free-text citation → DOI pipeline
|
|
102
|
+
│ │ ├── pdf.py # MIME sniff + streaming upload
|
|
103
|
+
│ │ ├── browser.py # Playwright headed window (lazy import; requires zotcli[browser])
|
|
104
|
+
│ │ ├── credentials.py # File-based credential store (mode 0600)
|
|
105
|
+
│ │ └── resolvers/
|
|
106
|
+
│ │ ├── crossref.py # DOI → CSL-JSON; bibliographic search
|
|
107
|
+
│ │ ├── arxiv.py # arXiv ID → CSL-JSON
|
|
108
|
+
│ │ ├── pubmed.py # PMID → CSL-JSON
|
|
109
|
+
│ │ ├── openlibrary.py # ISBN → CSL-JSON
|
|
110
|
+
│ │ ├── openalex.py # Citation/title fallback
|
|
111
|
+
│ │ ├── semantic_scholar.py # Second fallback (rate-limited)
|
|
112
|
+
│ │ ├── unpaywall.py # DOI → OA PDF URL (opt-in)
|
|
113
|
+
│ │ ├── ieee.py # URL → DOI extraction helpers
|
|
114
|
+
│ │ └── sciencedirect.py # URL/PII → DOI extraction helpers
|
|
71
115
|
│ ├── export/
|
|
72
116
|
│ │ ├── json_.py # Full-fidelity JSON dump
|
|
73
117
|
│ │ ├── csv_.py # Flat CSV (one row per item)
|
|
@@ -76,6 +120,8 @@ zotcli/
|
|
|
76
120
|
│ └── cli/
|
|
77
121
|
│ ├── main.py # Root Click group + global options
|
|
78
122
|
│ ├── render.py # Shared Rich helpers (tables, panels, trees)
|
|
123
|
+
│ ├── add.py # `zot add` group + auto-detect dispatcher
|
|
124
|
+
│ ├── config_cmd.py # `zot config` group
|
|
79
125
|
│ ├── collections.py # `zot collections` subcommands
|
|
80
126
|
│ ├── items.py # `zot items` subcommands
|
|
81
127
|
│ ├── attachments.py # `zot attachments` subcommands
|
|
@@ -84,10 +130,9 @@ zotcli/
|
|
|
84
130
|
│ └── export.py # `zot export` subcommands
|
|
85
131
|
└── tests/
|
|
86
132
|
├── conftest.py # In-memory SQLite fixture with seeded test data
|
|
87
|
-
├──
|
|
88
|
-
├──
|
|
89
|
-
|
|
90
|
-
└── test_cli.py # CLI integration tests via CliRunner
|
|
133
|
+
├── unit/ # Unit tests (no network, no Zotero)
|
|
134
|
+
├── integration/ # Integration tests (mocked connector + resolvers)
|
|
135
|
+
└── e2e/ # End-to-end (opt-in; requires real Zotero)
|
|
91
136
|
```
|
|
92
137
|
|
|
93
138
|
---
|
|
@@ -102,9 +147,18 @@ Query layer (queries/)
|
|
|
102
147
|
Database layer (db.py) ← read-only URI: file:zotero.sqlite?mode=ro
|
|
103
148
|
↓
|
|
104
149
|
zotero.sqlite
|
|
150
|
+
|
|
151
|
+
Write path (opt-in):
|
|
152
|
+
CLI layer (cli/add.py)
|
|
153
|
+
↓ resolve identifiers via external APIs
|
|
154
|
+
Resolver pipeline (write/resolvers/)
|
|
155
|
+
↓ CSL-JSON
|
|
156
|
+
Connector client (write/connector_client.py)
|
|
157
|
+
↓ loopback HTTP
|
|
158
|
+
Zotero desktop app → zotero.sqlite + storage/
|
|
105
159
|
```
|
|
106
160
|
|
|
107
|
-
|
|
161
|
+
See [`docs/architecture-write.md`](docs/architecture-write.md) for the full write-path design.
|
|
108
162
|
|
|
109
163
|
---
|
|
110
164
|
|
|
@@ -114,6 +168,9 @@ These go **before** the subcommand:
|
|
|
114
168
|
|
|
115
169
|
```
|
|
116
170
|
zot [--db PATH] [--library ID] [--format table|json|csv] [--no-color] <command>
|
|
171
|
+
|
|
172
|
+
Write-related globals (only relevant when write.enabled=true):
|
|
173
|
+
zot [--allow-write] [--connector-url URL] [--require-zotero/--no-require-zotero] <command>
|
|
117
174
|
```
|
|
118
175
|
|
|
119
176
|
---
|
|
@@ -296,68 +353,23 @@ zot search "bayesian" --field title
|
|
|
296
353
|
│ │ │ │ for Transformer Tap Position Estimation │ │ │
|
|
297
354
|
│ 2 │ 6BR3CYQA │ conferencePaper │ Bayesian distribution system state │ Angioni et al. │ 2016 │
|
|
298
355
|
│ │ │ │ estimation in presence of non-Gaussian… │ │ │
|
|
299
|
-
│ 3 │ K2YXXPX7 │ journalArticle │ A Recursive Bayesian Approach for │ Singh et al. │ 2010 │
|
|
300
|
-
│ │ │ │ Identification of Network Configuration… │ │ │
|
|
301
|
-
│ 4 │ CRJWMH2X │ journalArticle │ Real-Time Topology Estimation Using │ Liu et al. │ 2023 │
|
|
302
|
-
│ │ │ │ Graph-Bank Transformer… │ │ │
|
|
303
|
-
│ 5 │ TJH8CGUT │ conferencePaper │ Bayesian Methods for the Identification │ Brouillon │ 2021 │
|
|
304
|
-
│ │ │ │ of Distribution Networks │ et al. │ │
|
|
305
356
|
└────┴───────────┴─────────────────┴───────────────────────────────────────────┴────────────────┴────────┘
|
|
306
357
|
```
|
|
307
358
|
|
|
308
|
-
**By author name
|
|
359
|
+
**By author name:**
|
|
309
360
|
```bash
|
|
310
361
|
zot search --author "Numair"
|
|
311
362
|
```
|
|
312
|
-
```
|
|
313
|
-
12 result(s)
|
|
314
|
-
┏━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━┓
|
|
315
|
-
┃ # ┃ Key ┃ Type ┃ Title ┃ Authors ┃ Year ┃
|
|
316
|
-
┡━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━┩
|
|
317
|
-
│ 1 │ MDLX3G4P │ conferencePaper │ A Proposed IoT Architecture for Effective │ Numair et al. │ 2020 │
|
|
318
|
-
│ │ │ │ Energy Management in Smart Microgrids │ │ │
|
|
319
|
-
│ 2 │ W3P98AE9 │ conferencePaper │ On the UK smart metering system and value │ Numair et al. │ 2023 │
|
|
320
|
-
│ │ │ │ of data for distribution system… │ │ │
|
|
321
|
-
│ 3 │ UIMHSHNV │ journalArticle │ Fault Detection and Localisation in LV │ Numair et al. │ 2023 │
|
|
322
|
-
│ │ │ │ Distribution Networks Using Smart Meter… │ │ │
|
|
323
|
-
│ 4 │ TAVRNAY6 │ bookSection │ Infrastructure for the 4th Industrial │ Numair et al. │ 2024 │
|
|
324
|
-
│ │ │ │ Revolution Technologies │ │ │
|
|
325
|
-
│ 5 │ 5UFZMSLU │ journalArticle │ UNLOCKING DATA CENTRE HOSTING CAPACITY… │ Numair et al. │ 2026 │
|
|
326
|
-
│ … │ … │ … │ … │ … │ … │
|
|
327
|
-
└────┴───────────┴─────────────────┴───────────────────────────────────────────┴───────────────┴────────┘
|
|
328
|
-
```
|
|
329
363
|
|
|
330
364
|
**By DOI:**
|
|
331
365
|
```bash
|
|
332
366
|
zot search --doi "10.1016/j.epsr.2020.106394"
|
|
333
367
|
```
|
|
334
|
-
```
|
|
335
|
-
1 result(s)
|
|
336
|
-
┏━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━┓
|
|
337
|
-
┃ # ┃ Key ┃ Type ┃ Title ┃ Authors ┃ Year ┃
|
|
338
|
-
┡━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━┩
|
|
339
|
-
│ 1 │ ZGYGL35J │ journalArticle │ Preventative high impedance fault │ Langeroudi │ 2020 │
|
|
340
|
-
│ │ │ │ detection using distribution system… │ et al. │ │
|
|
341
|
-
└────┴───────────┴────────────────┴───────────────────────────────────────────┴──────────────────┴────────┘
|
|
342
|
-
```
|
|
343
368
|
|
|
344
369
|
**By year range:**
|
|
345
370
|
```bash
|
|
346
371
|
zot search --year 2023 --type conferencePaper
|
|
347
372
|
```
|
|
348
|
-
```
|
|
349
|
-
361 result(s) [truncated to first 5 shown]
|
|
350
|
-
┏━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━┓
|
|
351
|
-
┃ # ┃ Key ┃ Type ┃ Title ┃ Authors ┃ Year ┃
|
|
352
|
-
┡━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━┩
|
|
353
|
-
│ 1 │ ZRF8IFPI │ journalArticle │ Fault Location Method for an Active │ Zhao et al. │ 2023 │
|
|
354
|
-
│ │ │ │ Distribution Network Based on… │ │ │
|
|
355
|
-
│ 2 │ G3USAIB9 │ journalArticle │ PMU Measurements-Based Short-Term │ Li et al. │ 2023 │
|
|
356
|
-
│ │ │ │ Voltage Stability Assessment… │ │ │
|
|
357
|
-
│ 3 │ RBUU6AM2 │ journalArticle │ New coordination framework for smart │ Hussain et al. │ 2023 │
|
|
358
|
-
│ │ │ │ home peer-to-peer trading… │ │ │
|
|
359
|
-
└────┴───────────┴─────────────────┴───────────────────────────────────────────┴─────────────────┴────────┘
|
|
360
|
-
```
|
|
361
373
|
|
|
362
374
|
---
|
|
363
375
|
|
|
@@ -371,20 +383,6 @@ zot attachments path 5UFZMSLU
|
|
|
371
383
|
~/Zotero/storage/RIB344FW/Numair et al. - 2026 - UNLOCKING DATA CENTRE HOSTING CAPACITY AND FLEXIBILITY THROUGH DYNAMIC CABLE RATING.pdf
|
|
372
384
|
```
|
|
373
385
|
|
|
374
|
-
**List all attachments for an item (with existence check):**
|
|
375
|
-
```bash
|
|
376
|
-
zot items attachments W3P98AE9
|
|
377
|
-
```
|
|
378
|
-
```
|
|
379
|
-
Attachments for W3P98AE9
|
|
380
|
-
┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
381
|
-
┃ Key ┃ Type ┃ Mode ┃ Exists ┃ Path ┃
|
|
382
|
-
┡━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
|
|
383
|
-
│ GKHSKIP4 │ text/html │ imported_url │ ✓ │ …/storage/GKHSKIP4/10136487.html │
|
|
384
|
-
│ 7PZKGWIJ │ application/pdf │ imported_url │ ✓ │ …/storage/7PZKGWIJ/Numair et al. - 2023… │
|
|
385
|
-
└──────────┴─────────────────┴──────────────┴────────┴───────────────────────────────────────────┘
|
|
386
|
-
```
|
|
387
|
-
|
|
388
386
|
**Find all missing attachments:**
|
|
389
387
|
```bash
|
|
390
388
|
zot attachments list --missing
|
|
@@ -393,7 +391,6 @@ zot attachments list --missing
|
|
|
393
391
|
**Open a PDF in the system viewer:**
|
|
394
392
|
```bash
|
|
395
393
|
zot attachments open 5UFZMSLU
|
|
396
|
-
# Opening: ~/Zotero/storage/RIB344FW/Numair et al. - 2026 - ...pdf
|
|
397
394
|
```
|
|
398
395
|
|
|
399
396
|
---
|
|
@@ -403,76 +400,20 @@ zot attachments open 5UFZMSLU
|
|
|
403
400
|
**BibTeX:**
|
|
404
401
|
```bash
|
|
405
402
|
zot export bib --collection "Energy Market" --output refs.bib
|
|
406
|
-
|
|
407
|
-
```bibtex
|
|
408
|
-
@article{hussain_new_2023,
|
|
409
|
-
title = {New coordination framework for smart home peer-to-peer trading…},
|
|
410
|
-
author = {Hussain, Sadam and Azim, M. Imran and Lai, Chunyan and Eicker, Ursula},
|
|
411
|
-
year = {2023},
|
|
412
|
-
journal = {Energy},
|
|
413
|
-
volume = {284},
|
|
414
|
-
pages = {129297},
|
|
415
|
-
doi = {10.1016/j.energy.2023.129297},
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
@article{tsaousoglou_integrating_2023,
|
|
419
|
-
title = {Integrating Distributed Flexibility into TSO-DSO Coordinated Electricity Markets},
|
|
420
|
-
author = {Tsaousoglou, Georgios and Junker, Rune and …},
|
|
421
|
-
year = {2023},
|
|
422
|
-
doi = {10.1109/TEMPR.2023.3319673},
|
|
423
|
-
}
|
|
424
|
-
…
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
**CSV:**
|
|
428
|
-
```bash
|
|
429
|
-
zot export csv --collection "Energy Market" --output refs.csv
|
|
430
|
-
```
|
|
431
|
-
```
|
|
432
|
-
item_id,key,item_type,title,year,doi,journal,…,author_1,author_2,…,tags
|
|
433
|
-
10,RBUU6AM2,journalArticle,New coordination framework…,2023,10.1016/…,Energy,…,"Hussain, Sadam","Azim, M. Imran",…,Smart grid;Flexibility
|
|
434
|
-
18,8DE2V7ZZ,journalArticle,Integrating Distributed Flexibility…,2023,10.1109/…,…
|
|
403
|
+
zot export bib --item 5UFZMSLU --output ref.bib
|
|
435
404
|
```
|
|
436
405
|
|
|
437
|
-
**
|
|
406
|
+
**CSV / JSON / Markdown:**
|
|
438
407
|
```bash
|
|
439
|
-
zot export
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
[
|
|
443
|
-
{
|
|
444
|
-
"item_id": 10,
|
|
445
|
-
"key": "RBUU6AM2",
|
|
446
|
-
"item_type": "journalArticle",
|
|
447
|
-
"title": "New coordination framework for smart home peer-to-peer trading…",
|
|
448
|
-
"year": "2023",
|
|
449
|
-
"attachments": [
|
|
450
|
-
{
|
|
451
|
-
"key": "PXW7M26P",
|
|
452
|
-
"content_type": "application/pdf",
|
|
453
|
-
"file_exists": true,
|
|
454
|
-
"absolute_path": "~/Zotero/storage/PXW7M26P/Hussain et al. - 2023 - …pdf"
|
|
455
|
-
}
|
|
456
|
-
],
|
|
457
|
-
"notes": [],
|
|
458
|
-
"tags": ["Smart grid", "Distribution transformer", "Flexibility"],
|
|
459
|
-
…
|
|
460
|
-
}
|
|
461
|
-
]
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
**Markdown:**
|
|
465
|
-
```bash
|
|
466
|
-
zot export markdown --collection "Energy Market" --output refs.md
|
|
467
|
-
zot export markdown --all --notes --output full-library.md # include notes sections
|
|
408
|
+
zot export csv --collection "Energy Market" --output refs.csv
|
|
409
|
+
zot export json --all --output library.json
|
|
410
|
+
zot export markdown --all --notes --output report.md
|
|
468
411
|
```
|
|
469
412
|
|
|
470
413
|
---
|
|
471
414
|
|
|
472
415
|
### Workflow: search → get attachment paths
|
|
473
416
|
|
|
474
|
-
Find all papers with "bayesian" in the title **or** authored by "Numair", then print the PDF path for each:
|
|
475
|
-
|
|
476
417
|
```python
|
|
477
418
|
from zotcli.db import ZoteroDatabase
|
|
478
419
|
from zotcli.queries.search import search_items, search_by_author
|
|
@@ -493,13 +434,87 @@ with ZoteroDatabase(DB) as db:
|
|
|
493
434
|
print(f"{item.key}\t{att.absolute_path}")
|
|
494
435
|
```
|
|
495
436
|
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## Writing to your library
|
|
440
|
+
|
|
441
|
+
> **Default: strictly read-only.** Write capabilities are opt-in and route through Zotero's own connector HTTP server — `zotero.sqlite` is **never** modified directly.
|
|
442
|
+
|
|
443
|
+
**Zotero must be running** for any `zot add …` command to succeed.
|
|
444
|
+
|
|
445
|
+
### Enable write capability
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
# One-time setup (persists to config)
|
|
449
|
+
zot config set write.enabled true
|
|
450
|
+
|
|
451
|
+
# Check current status
|
|
452
|
+
zot config get write.enabled
|
|
453
|
+
|
|
454
|
+
# Verify Zotero is reachable
|
|
455
|
+
zot add status
|
|
496
456
|
```
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
457
|
+
|
|
458
|
+
### Quick-start: add items
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
# Add by DOI / arXiv / PMID / ISBN
|
|
462
|
+
zot add doi 10.1109/TPWRS.2023.1234567
|
|
463
|
+
zot add arxiv 2401.12345 --collection Preprints
|
|
464
|
+
zot add pmid 31452104
|
|
465
|
+
zot add isbn 978-0-262-03384-8 --collection Books
|
|
466
|
+
|
|
467
|
+
# IEEE Xplore or ScienceDirect URL (DOI is extracted automatically — no browser needed)
|
|
468
|
+
zot add "https://ieeexplore.ieee.org/document/9876543"
|
|
469
|
+
zot add "https://www.sciencedirect.com/science/article/pii/S2352467725000XYZ"
|
|
470
|
+
|
|
471
|
+
# Free-text citation string
|
|
472
|
+
zot add "Zhang, J., Geth, F., Heidari, R., Verbič, G. (2025) Beyond simplifications…"
|
|
473
|
+
|
|
474
|
+
# Local PDF (Zotero auto-recognises the parent reference)
|
|
475
|
+
zot add ~/Downloads/paper.pdf
|
|
476
|
+
|
|
477
|
+
# Smart auto-detect: zot add figures out the type automatically
|
|
478
|
+
zot add "10.1109/TPWRS.2023.1234567" # detected as DOI
|
|
479
|
+
zot add "2401.12345" # detected as arXiv
|
|
480
|
+
zot add "/home/me/paper.pdf" # detected as file
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Batch add
|
|
484
|
+
|
|
485
|
+
```bash
|
|
486
|
+
# papers.txt: one DOI / arXiv / URL / citation per line; # = comment
|
|
487
|
+
zot add batch papers.txt --collection "Smart Grid" --tag imported
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### Import from a bibliography file
|
|
491
|
+
|
|
492
|
+
```bash
|
|
493
|
+
# .bib, .ris, or .json (CSL-JSON)
|
|
494
|
+
zot add import refs.bib --collection "Imports/2026-05"
|
|
501
495
|
```
|
|
502
496
|
|
|
497
|
+
### Optional: paywalled-PDF retrieval
|
|
498
|
+
|
|
499
|
+
```bash
|
|
500
|
+
# Step 1: register for Unpaywall (email only; opt-in)
|
|
501
|
+
zot add login --service unpaywall
|
|
502
|
+
|
|
503
|
+
# Step 2: add with open-access PDF
|
|
504
|
+
zot add doi 10.1109/X --with-pdf
|
|
505
|
+
|
|
506
|
+
# For paywalled content: authenticate via browser SSO (requires zotcli[browser])
|
|
507
|
+
zot add login --service ieee # opens headed Chromium; sign in once
|
|
508
|
+
zot add login --service sciencedirect
|
|
509
|
+
zot add doi 10.1109/X --with-pdf # re-uses saved cookies
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Architecture summary
|
|
513
|
+
|
|
514
|
+
All writes go through `POST /connector/saveItems` (and related endpoints) on Zotero's local HTTP server at `127.0.0.1:23119`. Zotero performs every database transaction. `zotero.sqlite` is never opened in write mode by zotcli.
|
|
515
|
+
|
|
516
|
+
See [`docs/architecture-write.md`](docs/architecture-write.md) for full details.
|
|
517
|
+
|
|
503
518
|
---
|
|
504
519
|
|
|
505
520
|
## Running tests
|
|
@@ -508,33 +523,46 @@ W3P98AE9 ~/Zotero/storage/7PZKGWIJ/Numair et al. - 2023 - On the UK smart met
|
|
|
508
523
|
python3 -m pytest tests/ -q
|
|
509
524
|
```
|
|
510
525
|
```
|
|
511
|
-
|
|
512
|
-
40 passed in 31s
|
|
526
|
+
441 passed in Xs
|
|
513
527
|
```
|
|
514
528
|
|
|
515
|
-
Tests use an in-memory SQLite fixture seeded with synthetic Zotero data. No real database needed.
|
|
529
|
+
Tests use an in-memory SQLite fixture seeded with synthetic Zotero data. No real database needed. e2e tests (requiring a live Zotero) are opt-in:
|
|
530
|
+
|
|
531
|
+
```bash
|
|
532
|
+
python3 -m pytest tests/e2e -m e2e
|
|
533
|
+
```
|
|
516
534
|
|
|
517
535
|
---
|
|
518
536
|
|
|
519
537
|
## Configuration
|
|
520
538
|
|
|
521
|
-
|
|
539
|
+
Self-contained config at `<zotcli-home>/config.toml`. Run `zot config path` to find the directory.
|
|
522
540
|
|
|
523
541
|
```toml
|
|
524
542
|
[database]
|
|
525
|
-
path = "
|
|
526
|
-
library_id = 1
|
|
543
|
+
path = "" # empty = auto-detect zotero.sqlite
|
|
527
544
|
|
|
528
|
-
[
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
545
|
+
[write]
|
|
546
|
+
enabled = false # opt-in; set true once with: zot config set write.enabled true
|
|
547
|
+
connector_url = "http://127.0.0.1:23119"
|
|
548
|
+
require_zotero = true
|
|
549
|
+
|
|
550
|
+
[unpaywall]
|
|
551
|
+
enabled = false # opt-in; set up with: zot add login --service unpaywall
|
|
552
|
+
email = ""
|
|
553
|
+
|
|
554
|
+
[browser]
|
|
555
|
+
headless = false # SSO/captcha needs headed browser
|
|
532
556
|
```
|
|
533
557
|
|
|
558
|
+
Override `<zotcli-home>` with the `ZOTCLI_HOME` environment variable.
|
|
559
|
+
|
|
534
560
|
---
|
|
535
561
|
|
|
536
562
|
## Safety
|
|
537
563
|
|
|
538
|
-
- Database opened with `sqlite3://…?mode=ro` — the OS-level read-only URI flag makes writes impossible
|
|
564
|
+
- Database opened with `sqlite3://…?mode=ro` — the OS-level read-only URI flag makes direct writes impossible
|
|
539
565
|
- WAL journal detection warns if Zotero is currently open (pending writes may not be visible yet)
|
|
540
|
-
-
|
|
566
|
+
- Write operations route through Zotero's own connector HTTP server — never via direct SQLite mutation
|
|
567
|
+
- Default is read-only; writes require explicit opt-in (`zot config set write.enabled true`)
|
|
568
|
+
- No Zotero API key required
|