scholarcli 1.24__tar.gz → 1.27__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.
Files changed (102) hide show
  1. {scholarcli-1.24 → scholarcli-1.27}/PKG-INFO +154 -5
  2. scholarcli-1.27/README.md +455 -0
  3. {scholarcli-1.24 → scholarcli-1.27}/pyproject.toml +7 -1
  4. {scholarcli-1.24 → scholarcli-1.27}/src/scholar/__init__.py +9 -0
  5. {scholarcli-1.24 → scholarcli-1.27}/src/scholar/cache.py +41 -8
  6. {scholarcli-1.24 → scholarcli-1.27}/src/scholar/cli.py +1163 -186
  7. {scholarcli-1.24 → scholarcli-1.27}/src/scholar/crossref.py +77 -29
  8. {scholarcli-1.24 → scholarcli-1.27}/src/scholar/enrich.py +42 -19
  9. {scholarcli-1.24 → scholarcli-1.27}/src/scholar/llm_review.py +338 -136
  10. scholarcli-1.27/src/scholar/network.py +226 -0
  11. {scholarcli-1.24 → scholarcli-1.27}/src/scholar/notes.py +176 -25
  12. {scholarcli-1.24 → scholarcli-1.27}/src/scholar/pdf.py +172 -95
  13. scholarcli-1.27/src/scholar/preprint.py +220 -0
  14. {scholarcli-1.24 → scholarcli-1.27}/src/scholar/providers.py +1785 -285
  15. scholarcli-1.27/src/scholar/ratelimit.py +484 -0
  16. scholarcli-1.27/src/scholar/review.py +3003 -0
  17. {scholarcli-1.24 → scholarcli-1.27}/src/scholar/scholar.py +246 -17
  18. {scholarcli-1.24 → scholarcli-1.27}/src/scholar/tui.py +124 -36
  19. scholarcli-1.27/src/scholar/tutorials/enrich-verify-export.md +162 -0
  20. scholarcli-1.27/src/scholar/tutorials/first-search.md +190 -0
  21. scholarcli-1.27/src/scholar/tutorials/llm-synthesis.md +316 -0
  22. scholarcli-1.27/src/scholar/tutorials/reviewing-in-the-tui.md +206 -0
  23. scholarcli-1.27/src/scholar/tutorials/scholar-with-an-agent.md +286 -0
  24. scholarcli-1.27/src/scholar/unpaywall.py +198 -0
  25. scholarcli-1.27/src/scholar/utils.py +511 -0
  26. {scholarcli-1.24 → scholarcli-1.27}/src/scholarcli.egg-info/PKG-INFO +154 -5
  27. {scholarcli-1.24 → scholarcli-1.27}/src/scholarcli.egg-info/SOURCES.txt +13 -0
  28. {scholarcli-1.24 → scholarcli-1.27}/src/scholarcli.egg-info/requires.txt +3 -0
  29. scholarcli-1.27/tests/test_cache.py +210 -0
  30. {scholarcli-1.24 → scholarcli-1.27}/tests/test_cli.py +1489 -0
  31. {scholarcli-1.24 → scholarcli-1.27}/tests/test_crossref.py +107 -3
  32. {scholarcli-1.24 → scholarcli-1.27}/tests/test_enrich.py +102 -0
  33. {scholarcli-1.24 → scholarcli-1.27}/tests/test_llm_review.py +498 -10
  34. scholarcli-1.27/tests/test_network.py +193 -0
  35. {scholarcli-1.24 → scholarcli-1.27}/tests/test_notes.py +235 -0
  36. {scholarcli-1.24 → scholarcli-1.27}/tests/test_pdf.py +167 -24
  37. scholarcli-1.27/tests/test_preprint.py +302 -0
  38. {scholarcli-1.24 → scholarcli-1.27}/tests/test_providers.py +1886 -24
  39. scholarcli-1.27/tests/test_ratelimit.py +419 -0
  40. scholarcli-1.27/tests/test_review.py +2834 -0
  41. {scholarcli-1.24 → scholarcli-1.27}/tests/test_scholar.py +349 -0
  42. scholarcli-1.27/tests/test_tui.py +204 -0
  43. scholarcli-1.27/tests/test_unpaywall.py +286 -0
  44. scholarcli-1.27/tests/test_utils.py +391 -0
  45. scholarcli-1.24/README.md +0 -309
  46. scholarcli-1.24/src/scholar/review.py +0 -1830
  47. scholarcli-1.24/src/scholar/utils.py +0 -203
  48. scholarcli-1.24/tests/test_cache.py +0 -98
  49. scholarcli-1.24/tests/test_review.py +0 -1500
  50. scholarcli-1.24/tests/test_tui.py +0 -31
  51. scholarcli-1.24/tests/test_utils.py +0 -154
  52. {scholarcli-1.24 → scholarcli-1.27}/LICENSE +0 -0
  53. {scholarcli-1.24 → scholarcli-1.27}/setup.cfg +0 -0
  54. {scholarcli-1.24 → scholarcli-1.27}/src/scholar/__main__.py +0 -0
  55. {scholarcli-1.24 → scholarcli-1.27}/src/scholar/questionary.py +0 -0
  56. {scholarcli-1.24 → scholarcli-1.27}/src/scholarcli.egg-info/dependency_links.txt +0 -0
  57. {scholarcli-1.24 → scholarcli-1.27}/src/scholarcli.egg-info/entry_points.txt +0 -0
  58. {scholarcli-1.24 → scholarcli-1.27}/src/scholarcli.egg-info/top_level.txt +0 -0
  59. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/__init__.py +0 -0
  60. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/apis/__init__.py +0 -0
  61. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/apis/aggregator.py +0 -0
  62. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/apis/arxiv.py +0 -0
  63. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/apis/base.py +0 -0
  64. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/apis/crossref.py +0 -0
  65. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/apis/google_scholar.py +0 -0
  66. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/apis/openalex.py +0 -0
  67. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/apis/opencitations.py +0 -0
  68. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/apis/semantic_scholar.py +0 -0
  69. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/cli.py +0 -0
  70. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/exporters/__init__.py +0 -0
  71. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/exporters/bibtex.py +0 -0
  72. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/exporters/csv_exporter.py +0 -0
  73. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/exporters/tikz.py +0 -0
  74. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/filters/__init__.py +0 -0
  75. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/filters/filter_engine.py +0 -0
  76. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/models.py +0 -0
  77. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/paper_utils.py +0 -0
  78. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/parsers/__init__.py +0 -0
  79. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/parsers/pdf_parser.py +0 -0
  80. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/scoring/__init__.py +0 -0
  81. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/scoring/base.py +0 -0
  82. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/scoring/llm_scorer.py +0 -0
  83. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/scoring/tfidf_scorer.py +0 -0
  84. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/services.py +0 -0
  85. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/snowballing.py +0 -0
  86. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/storage/__init__.py +0 -0
  87. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/storage/json_storage.py +0 -0
  88. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/tui/__init__.py +0 -0
  89. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/tui/app.py +0 -0
  90. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/tui/dialogs.py +0 -0
  91. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/tui/setup.py +0 -0
  92. {scholarcli-1.24 → scholarcli-1.27}/src/snowball/visualization.py +0 -0
  93. {scholarcli-1.24 → scholarcli-1.27}/src/tuxedo/__init__.py +0 -0
  94. {scholarcli-1.24 → scholarcli-1.27}/src/tuxedo/analysis.py +0 -0
  95. {scholarcli-1.24 → scholarcli-1.27}/src/tuxedo/cli.py +0 -0
  96. {scholarcli-1.24 → scholarcli-1.27}/src/tuxedo/clustering.py +0 -0
  97. {scholarcli-1.24 → scholarcli-1.27}/src/tuxedo/database.py +0 -0
  98. {scholarcli-1.24 → scholarcli-1.27}/src/tuxedo/grobid.py +0 -0
  99. {scholarcli-1.24 → scholarcli-1.27}/src/tuxedo/logging.py +0 -0
  100. {scholarcli-1.24 → scholarcli-1.27}/src/tuxedo/models.py +0 -0
  101. {scholarcli-1.24 → scholarcli-1.27}/src/tuxedo/project.py +0 -0
  102. {scholarcli-1.24 → scholarcli-1.27}/src/tuxedo/tui.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scholarcli
3
- Version: 1.24
3
+ Version: 1.27
4
4
  Summary: A tool for structured literature searches across bibliographic databases
5
5
  Author-email: Daniel Bosk <dbosk@kth.se>, Ric Glassey <glassey@kth.se>
6
6
  License-Expression: MIT
@@ -14,6 +14,7 @@ Requires-Dist: pyalex>=0.19
14
14
  Requires-Dist: arxiv>=2.1.0
15
15
  Requires-Dist: cachetools>=6.2.4
16
16
  Requires-Dist: platformdirs>=4.5.1
17
+ Requires-Dist: filelock>=3.12
17
18
  Requires-Dist: textual>=6.11.0
18
19
  Requires-Dist: pypandoc>=1.14
19
20
  Requires-Dist: click>=8.0.0
@@ -36,6 +37,8 @@ Requires-Dist: pyyaml>=6.0
36
37
  Requires-Dist: questionary>=2.0.0
37
38
  Requires-Dist: llm-github-copilot>=0.3.1
38
39
  Requires-Dist: llm-mistral<0.16,>=0.15
40
+ Requires-Dist: llm-openai-via-codex>=0.1a0
41
+ Requires-Dist: pytorial>=0.8.1
39
42
  Dynamic: license-file
40
43
 
41
44
  # Scholar
@@ -46,19 +49,36 @@ A command-line tool for conducting structured literature searches across multipl
46
49
 
47
50
  ### Multi-Database Search
48
51
 
49
- Search across six academic databases with a single query:
52
+ Search across fifteen academic databases with a single query:
50
53
 
51
54
  - **Semantic Scholar** - AI-powered research database with 200M+ papers
52
55
  - **OpenAlex** - Open catalog of 250M+ scholarly works
53
56
  - **DBLP** - Computer science bibliography
54
57
  - **Web of Science** - Comprehensive citation index (requires API key)
55
58
  - **IEEE Xplore** - IEEE technical literature (requires API key)
56
- - **arXiv** - Preprints (no API key)
59
+ - **Scopus** - Elsevier's abstract and citation database (requires API key)
60
+ - **arXiv** - Preprints in physics, math, CS (no API key)
61
+ - **HAL** - The French national open archive (no API key)
62
+ - **Preprint servers** - SSRN, bioRxiv, medRxiv, ChemRxiv, Research
63
+ Square, Preprints.org, and the IACR Cryptology ePrint Archive, each
64
+ searched through OpenAlex pinned to that server (none of them offers
65
+ a usable search API of its own)
66
+
67
+ The preprint-server providers are opt-in: a default search already finds
68
+ their papers through OpenAlex, so they run only when you name them —
69
+ either individually (`-p ssrn` scopes a search to SSRN) or all at once
70
+ with the `preprints` group.
57
71
 
58
72
  ```bash
59
73
  # Search specific providers
60
74
  scholar search "federated learning" -p s2 -p openalex
61
75
 
76
+ # Scope a search to one preprint server
77
+ scholar search "corporate governance" -p ssrn
78
+
79
+ # All preprint servers at once (arXiv, HAL, SSRN, bioRxiv, medRxiv, ...)
80
+ scholar search "diffusion models" -p preprints
81
+
62
82
  # Start from a research question (LLM generates provider-specific queries)
63
83
  scholar rq "How can privacy-preserving ML be evaluated?" \
64
84
  --provider openalex --provider dblp \
@@ -109,6 +129,16 @@ scholar sessions resume "machine learning"
109
129
 
110
130
  # Export session to reports
111
131
  scholar sessions export "machine learning" -f all
132
+
133
+ # LaTeX report as an \input-able fragment (writes review.tex + review.bib)
134
+ scholar sessions export "machine learning" -f latex --no-standalone -o review
135
+
136
+ # Audit table for a backing appendix (one row per paper with the reason;
137
+ # always a fragment; --lang en|sv, --theme TAG=NAME gives themes readable names)
138
+ scholar sessions export "machine learning" -f table --lang sv --label sok-A \
139
+ --track "sökspår 1" --theme ml=maskininlärning -o review-full
140
+ # ... listing only the cited/supporting/qualifying rows, the rest as counts
141
+ scholar sessions export "machine learning" -f table --bearing-only -o review-bearing
112
142
  ```
113
143
 
114
144
  ### Paper Notes
@@ -133,12 +163,25 @@ Search results are cached to avoid redundant API calls:
133
163
 
134
164
  ```bash
135
165
  scholar cache info # Show cache statistics
136
- scholar cache clear # Delete cached results
166
+ scholar cache clear # Delete cached results (keeps paid LLM output;
167
+ # add --include-llm to delete that too)
137
168
  scholar cache path # Print cache directory
138
169
  ```
139
170
 
140
171
  PDF downloads are also cached for offline viewing.
141
172
 
173
+ ### Rate-Limit and Quota Tracking
174
+
175
+ Provider rate limits and daily quotas (e.g. IEEE's 200 requests/day) are
176
+ tracked persistently across invocations: a provider whose budget is spent
177
+ is skipped with a warning naming the reset time instead of firing doomed
178
+ requests.
179
+
180
+ ```bash
181
+ scholar providers limits # Show usage, remaining budget, reset times
182
+ scholar providers limits --reset ieee # Clear tracked state for one provider
183
+ ```
184
+
142
185
  ## Quickstart
143
186
 
144
187
  ### Install
@@ -188,6 +231,30 @@ scholar search "machine learning privacy"
188
231
  scholar rq "How do LLMs support novice programming?" --count 20
189
232
  ```
190
233
 
234
+ ### Tutorials
235
+
236
+ Scholar ships interactive tutorials (built on
237
+ [pytorial](https://github.com/dbosk/pytorial)): each step opens a shell,
238
+ you type the command the step asks for, and the transcript is checked
239
+ before the lesson moves on.
240
+
241
+ ```bash
242
+ scholar tutorial list # the lessons and your progress
243
+ scholar tutorial run first-search # start or resume a lesson
244
+ scholar tutorial run --restart first-search
245
+ scholar tutorial review first-search # read back what you typed
246
+ ```
247
+
248
+ The lessons, in order: `first-search` (search, providers, output formats,
249
+ saving a session), `reviewing-in-the-tui` (recording decisions),
250
+ `enrich-verify-export` (abstracts and open access, retractions, CSV and
251
+ LaTeX exports), `llm-synthesis` (setting up the `llm` command, LLM
252
+ classification, a literature overview) and `scholar-with-an-agent` (what
253
+ an agent running the `backing-claims` skill does with Scholar, and how to
254
+ audit it). Every lesson runs in its own workspace with its own session
255
+ store, so practice sessions never appear in your real `scholar sessions
256
+ list`.
257
+
191
258
  ## Installation
192
259
 
193
260
  If you don't use `pipx`, you can install with `pip`:
@@ -204,15 +271,34 @@ uv pip install scholarcli
204
271
 
205
272
  ## Configuration
206
273
 
274
+ Set `SCHOLAR_EMAIL` to a contact address: OpenAlex (and thus every
275
+ preprint-server provider), Crossref, and the polite pools use it for
276
+ faster responses, and Unpaywall (open-access lookup during
277
+ `scholar enrich` and `scholar pdf open <doi>`) requires it. `OPENALEX_EMAIL`
278
+ and `CROSSREF_MAILTO` are still honoured as per-service overrides.
279
+
280
+ OpenAlex meters requests against a daily budget (a search costs $0.001,
281
+ reset at midnight UTC). A free API key from
282
+ [openalex.org/settings/api](https://openalex.org/settings/api) raises the
283
+ budget tenfold; set it as `OPENALEX_API_KEY` and it is used by the
284
+ `openalex` provider and every preprint-server provider. `scholar
285
+ providers check` then shows how much of the day's budget is left, and a
286
+ search that hits the spent budget reports the reset time instead of a
287
+ bare 429.
288
+
207
289
  Some providers require API keys set as environment variables:
208
290
 
209
291
  | Provider | Environment Variable | Required | How to Get |
210
292
  |----------|---------------------|----------|------------|
211
293
  | Semantic Scholar | `S2_API_KEY` | No | [api.semanticscholar.org](https://api.semanticscholar.org) |
212
- | OpenAlex | `OPENALEX_EMAIL` | No | Any email (for polite pool) |
294
+ | OpenAlex | `OPENALEX_API_KEY`, `SCHOLAR_EMAIL` | No | [openalex.org/settings/api](https://openalex.org/settings/api) (10x daily budget); any email (polite pool) |
213
295
  | DBLP | - | No | No key needed |
296
+ | arXiv | - | No | No key needed |
297
+ | HAL | - | No | No key needed |
298
+ | SSRN, bioRxiv, medRxiv, ChemRxiv, Research Square, Preprints.org, IACR | `SCHOLAR_EMAIL` | No | Any email (they search via OpenAlex) |
214
299
  | Web of Science | `WOS_EXPANDED_API_KEY` or `WOS_STARTER_API_KEY` | Yes | [developer.clarivate.com](https://developer.clarivate.com) |
215
300
  | IEEE Xplore | `IEEE_API_KEY` | Yes | [developer.ieee.org](https://developer.ieee.org) |
301
+ | Scopus | `SCOPUS_API_KEY` | Yes | [dev.elsevier.com](https://dev.elsevier.com). Searching works from any network, but abstracts, keywords and full author lists come only with Scopus's COMPLETE view, which is bound to the institution's IP range (or `SCOPUS_INST_TOKEN`). scholar asks for it and falls back by itself (`scholar providers check` tells which view you get); `SCOPUS_VIEW=standard` or `complete` overrides. A 400 "Error translating query" on *every* query is an Elsevier-side refusal, not a query error: it has cleared by itself before, so retry later |
216
302
 
217
303
  View provider status:
218
304
 
@@ -220,6 +306,38 @@ View provider status:
220
306
  scholar providers
221
307
  ```
222
308
 
309
+ ### Network: proxy and User-Agent
310
+
311
+ To reach subscriber-only content from off campus (Scopus's COMPLETE
312
+ view, a publisher that admits the institution's IP range), route
313
+ scholar's requests through a proxy on that network, for example an SSH
314
+ SOCKS tunnel:
315
+
316
+ ```bash
317
+ ssh -D 127.0.0.1:1081 -N you@login.your-university.example &
318
+ scholar --proxy socks5h://127.0.0.1:1081 search "..."
319
+ export SCHOLAR_PROXY=socks5h://127.0.0.1:1081 # or for every command
320
+ scholar providers check --show-ip # route, exit IP, health
321
+ ```
322
+
323
+ `--proxy URL` (a global option, before the command) wins over
324
+ `SCHOLAR_PROXY`; with neither, the standard `HTTP(S)_PROXY`/`ALL_PROXY`
325
+ variables apply as usual. Use `socks5h://` rather than `socks5://` so
326
+ host names are resolved by the proxy, on the far side of the tunnel.
327
+ SOCKS needs the PySocks package (`pipx inject scholarcli PySocks`);
328
+ scholar refuses a SOCKS proxy without it rather than failing every
329
+ request. `scholar providers check` always prints the route; `--show-ip`
330
+ also asks api.ipify.org for the exit address (a third-party request, so
331
+ only on demand).
332
+
333
+ Requests identify themselves as
334
+ `scholar/<version> (+https://github.com/dbosk/scholar)`. Your
335
+ `SCHOLAR_EMAIL` is not in it, so publishers never see the address; only
336
+ the services that ask for one (OpenAlex, Crossref, Unpaywall) get it,
337
+ through their own parameters. Set `SCHOLAR_USER_AGENT` to replace it. Scholar does not imitate a browser: a Cloudflare challenge
338
+ or a login page from a publisher means the PDF is not available to
339
+ scripts, and scholar stops there.
340
+
223
341
  ## Usage Examples
224
342
 
225
343
  ### Basic Search
@@ -249,6 +367,15 @@ scholar sessions resume "privacy-ml-review"
249
367
 
250
368
  # 4. Generate reports
251
369
  scholar sessions export "privacy-ml-review" -f all
370
+
371
+ # 5. Synthesise the kept papers (needs a research context); the LaTeX
372
+ # document ends with the decision record as an appendix
373
+ scholar llm context "privacy-ml-review" "How is privacy preserved in ML?"
374
+ scholar llm synthesize "privacy-ml-review" -f latex -o synthesis.tex
375
+
376
+ # ... or as a fragment to \input into a thesis chapter (also writes
377
+ # synthesis.bib; add \addbibresource{synthesis.bib} to the preamble)
378
+ scholar llm synthesize "privacy-ml-review" -f latex --no-standalone -o synthesis.tex
252
379
  ```
253
380
 
254
381
  ### Enriching Results
@@ -263,6 +390,22 @@ scholar search "query" --enrich
263
390
  scholar enrich "session-name"
264
391
  ```
265
392
 
393
+ When no PDF can be found for a paper, enrichment falls back to preprint
394
+ versions: authoritative links (OpenAlex's alternate locations and
395
+ Crossref's `has-preprint` relation) are always followed, and with
396
+ `--preprint-search` the preprint servers are additionally searched by
397
+ title and authors (exact match only). An adopted preprint *replaces*
398
+ the entry — its DOI, venue, and PDF — so exported citations point at
399
+ the version you actually read; the published DOI is kept on the entry
400
+ for provenance, and `scholar verify` reports such entries as
401
+ `preprint of <doi>` rather than superseded.
402
+
403
+ ```bash
404
+ # Also search preprint servers for papers without a PDF
405
+ scholar enrich "session-name" --preprint-search
406
+ scholar search "query" --enrich --preprint-search
407
+ ```
408
+
266
409
  ### PDF Management
267
410
 
268
411
  ```bash
@@ -299,6 +442,12 @@ For large result sets, Scholar can use LLMs to assist with paper classification:
299
442
  # In the TUI, press 'L' to invoke LLM classification
300
443
  # Or use the CLI command directly
301
444
  scholar llm classify "session-name" --count 10
445
+
446
+ # Constrain the tags to a vocabulary (tags already in the session count too)
447
+ scholar llm classify "session-name" -t supports-claim -t qualifies-claim \
448
+ -t adjacent-subtopic -t off-topic-false-hit
449
+ # ... or let the model add tags of its own when none of them fits
450
+ scholar llm classify "session-name" --allow-new-tags
302
451
  ```
303
452
 
304
453
  ### How It Works