sciwrite-lint 0.2.1__tar.gz → 0.2.2__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 (82) hide show
  1. {sciwrite_lint-0.2.1/sciwrite_lint.egg-info → sciwrite_lint-0.2.2}/PKG-INFO +57 -2
  2. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/README.md +55 -1
  3. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/pyproject.toml +2 -1
  4. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/__init__.py +1 -1
  5. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cli/fetch.py +6 -0
  6. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cli/verify.py +36 -14
  7. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/fulltext.py +13 -1
  8. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/pipeline.py +5 -0
  9. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2/sciwrite_lint.egg-info}/PKG-INFO +57 -2
  10. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/LICENSE +0 -0
  11. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/__main__.py +0 -0
  12. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/_network.py +0 -0
  13. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/api.py +0 -0
  14. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/__init__.py +0 -0
  15. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/_section_utils.py +0 -0
  16. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/cite_purpose.py +0 -0
  17. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/claim_support.py +0 -0
  18. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/cross_section_consistency.py +0 -0
  19. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/dangling_cite.py +0 -0
  20. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/dangling_ref.py +0 -0
  21. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/full_paper_consistency.py +0 -0
  22. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/ref_internal_checks.py +0 -0
  23. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/reference_accuracy.py +0 -0
  24. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/reference_exists.py +0 -0
  25. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/reference_unreliable.py +0 -0
  26. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/registry.py +0 -0
  27. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/retracted_cite.py +0 -0
  28. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/structure_promises.py +0 -0
  29. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/unreferenced_figure.py +0 -0
  30. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/claims.py +0 -0
  31. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/claude_backend.py +0 -0
  32. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/claude_cli.py +0 -0
  33. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cli/__init__.py +0 -0
  34. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cli/check.py +0 -0
  35. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cli/config.py +0 -0
  36. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cli/misc.py +0 -0
  37. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cli/rank.py +0 -0
  38. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/config.py +0 -0
  39. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cross_paper.py +0 -0
  40. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/eval_claims.py +0 -0
  41. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/llm_utils.py +0 -0
  42. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/local_pdfs.py +0 -0
  43. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/manuscript_store.py +0 -0
  44. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/models.py +0 -0
  45. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/pdf/__init__.py +0 -0
  46. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/pdf/grobid.py +0 -0
  47. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/pdf/pdf_download.py +0 -0
  48. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/prompt_safety.py +0 -0
  49. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/py.typed +0 -0
  50. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/rate_limiter.py +0 -0
  51. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/__init__.py +0 -0
  52. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/citations.py +0 -0
  53. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/crossref.py +0 -0
  54. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/embedding_store.py +0 -0
  55. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/matching.py +0 -0
  56. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/metadata.py +0 -0
  57. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/reference_store.py +0 -0
  58. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/retraction_watch.py +0 -0
  59. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/workspace_db.py +0 -0
  60. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/report.py +0 -0
  61. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/schemas.py +0 -0
  62. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/scoring/__init__.py +0 -0
  63. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/scoring/chain.py +0 -0
  64. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/scoring/contribution.py +0 -0
  65. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/scoring/scilint_score.py +0 -0
  66. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/tex_parser.py +0 -0
  67. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/usage.py +0 -0
  68. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vision/__init__.py +0 -0
  69. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vision/cache.py +0 -0
  70. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vision/describe.py +0 -0
  71. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vision/image_extraction.py +0 -0
  72. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vision/pipeline.py +0 -0
  73. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vllm/__init__.py +0 -0
  74. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vllm/metrics.py +0 -0
  75. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vllm/vllm_server.py +0 -0
  76. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/web.py +0 -0
  77. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint.egg-info/SOURCES.txt +0 -0
  78. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint.egg-info/dependency_links.txt +0 -0
  79. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint.egg-info/entry_points.txt +0 -0
  80. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint.egg-info/requires.txt +0 -0
  81. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint.egg-info/top_level.txt +0 -0
  82. {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sciwrite-lint
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: A linter for scientific manuscripts — reference verification, consistency checking, and structural validation.
5
5
  Author: Sergey Samsonau
6
6
  Maintainer-email: Authentic Research Partners LLC <info@arpconnect.com>
@@ -9,6 +9,7 @@ Project-URL: Homepage, https://arpconnect.com
9
9
  Project-URL: Repository, https://github.com/authentic-research-partners/sciwrite-lint
10
10
  Project-URL: Documentation, https://github.com/authentic-research-partners/sciwrite-lint#readme
11
11
  Project-URL: Issues, https://github.com/authentic-research-partners/sciwrite-lint/issues
12
+ Project-URL: Paper, https://arxiv.org/abs/2604.08501
12
13
  Keywords: linter,latex,bibtex,pdf,scientific-writing,academic-writing,citation-checker,reference-verification,claim-verification,manuscript,peer-review,arxiv,vllm,qwen
13
14
  Classifier: Development Status :: 3 - Alpha
14
15
  Classifier: Intended Audience :: Developers
@@ -51,6 +52,8 @@ Dynamic: license-file
51
52
 
52
53
  # sciwrite-lint
53
54
 
55
+ [![arXiv](https://img.shields.io/badge/arXiv-2604.08501-b31b1b.svg)](https://arxiv.org/abs/2604.08501)
56
+
54
57
  A linter for scientific manuscripts. Checks that your references exist, your metadata is accurate, your citations support the claims you make, and your cited papers' own bibliographies are real. Works on LaTeX and PDF. Runs entirely on your machine. Produces a **SciLint Score**.
55
58
 
56
59
  The only open-source tool that combines reference verification, claim checking, manuscript consistency, figure analysis, and bibliography verification in one pipeline — powered entirely by a local LLM on your own GPU.
@@ -154,6 +157,32 @@ uv tool install sciwrite-lint --python 3.13
154
157
 
155
158
  uv downloads Python 3.13 automatically (does not affect any Python you may already have) and installs sciwrite-lint as a globally available command.
156
159
 
160
+ ### Reproducible environment (optional)
161
+
162
+ `pyproject.toml` declares lower bounds for dependencies, so `uv tool install` / `pip install` may resolve newer versions as upstream releases ship. If you want the exact versions the maintainer develops and tests against — for reproducing benchmarks, debugging version-drift issues, or CI — install from [`requirements-pinned.txt`](requirements-pinned.txt) instead.
163
+
164
+ **What's in it:** top-level packages declared in `pyproject.toml` pinned to `==` versions. Transitive dependencies are not pinned — pip resolves them within each top-level package's own constraints. This is a deliberate middle ground: it locks the packages sciwrite-lint actually imports and tests against, without over-constraining the dep tree.
165
+
166
+ ```bash
167
+ # Clone the public repo (required — uv tool install can't read a local file)
168
+ git clone https://github.com/authentic-research-partners/sciwrite-lint.git
169
+ cd sciwrite-lint
170
+
171
+ # With uv (recommended — fast, isolated, manages Python itself)
172
+ uv venv --python 3.13 .venv
173
+ source .venv/bin/activate
174
+ uv pip install -r requirements-pinned.txt
175
+ uv pip install -e . --no-deps # install sciwrite-lint source; deps already pinned
176
+
177
+ # Or with plain pip
178
+ python3.13 -m venv .venv
179
+ source .venv/bin/activate
180
+ pip install -r requirements-pinned.txt
181
+ pip install -e . --no-deps
182
+ ```
183
+
184
+ Activate the venv in each new terminal with `source .venv/bin/activate`. For most users, the plain `uv tool install sciwrite-lint` above is simpler — use the pinned file only when you need exact version reproduction.
185
+
157
186
  ## Setup
158
187
 
159
188
  ```bash
@@ -167,7 +196,18 @@ sciwrite-lint containers monitor # live dashboard: service health, VRAM, K
167
196
 
168
197
  `init` detects `.tex` files and their `.bib` references and generates a `.sciwrite-lint.toml` config. Review to confirm the right files were detected.
169
198
 
170
- **Paywalled references:** drop PDFs into `local_pdfs/` with filenames matching the reference title. The tool fuzzy-matches filenames against your `.bib` titles and uses local copies instead of downloading.
199
+ **Providing PDFs manually (`local_pdfs/`):** Drop any reference PDF into the `local_pdfs/` directory to skip the download step entirely. This is useful when you already have the file, when the paper is paywalled, or when the publisher blocks automated downloads (Cell, Springer, some PMC pages). Name files by the reference title:
200
+
201
+ ```
202
+ local_pdfs/
203
+ ├── States of Curiosity Modulate Hippocampus-Dependent Learning.pdf
204
+ ├── Mind in Society.pdf
205
+ └── The System of Professions.pdf
206
+ ```
207
+
208
+ The tool fuzzy-matches filenames against your `.bib` titles (threshold 0.80) and copies matches to the workspace. On the next run, matched references upgrade from T2 to T1 and go through GROBID parsing and claim verification like any other full-text reference.
209
+
210
+ **Finding what to download:** `sciwrite-lint verify` lists T2 references that are confirmed open access with direct URLs — open in your browser and save to `local_pdfs/`. For paywalled papers, use your institution's library access.
171
211
 
172
212
  **Optional API keys** increase rate limits for Semantic Scholar, NCBI, and CORE:
173
213
 
@@ -269,6 +309,21 @@ For contributors and advanced users:
269
309
  - [docs/evals.md](https://github.com/authentic-research-partners/sciwrite-lint/blob/main/docs/evals.md) — detection evaluation framework, adding test cases
270
310
  - [docs/calibration.md](https://github.com/authentic-research-partners/sciwrite-lint/blob/main/docs/calibration.md) — SciLint Score calibration against ground-truth papers
271
311
 
312
+ ## Citation
313
+
314
+ If you use sciwrite-lint in your research, please cite the accompanying paper ([arXiv:2604.08501](https://arxiv.org/abs/2604.08501)):
315
+
316
+ ```bibtex
317
+ @article{samsonau2026sciwritelint,
318
+ title = {sciwrite-lint: Verification Infrastructure for the Age of Science Vibe-Writing},
319
+ author = {Samsonau, Sergey V.},
320
+ journal = {arXiv preprint arXiv:2604.08501},
321
+ year = {2026},
322
+ }
323
+ ```
324
+
325
+ GitHub's "Cite this repository" button (powered by [`CITATION.cff`](CITATION.cff)) will also generate this for you.
326
+
272
327
  ## License
273
328
 
274
329
  MIT
@@ -1,5 +1,7 @@
1
1
  # sciwrite-lint
2
2
 
3
+ [![arXiv](https://img.shields.io/badge/arXiv-2604.08501-b31b1b.svg)](https://arxiv.org/abs/2604.08501)
4
+
3
5
  A linter for scientific manuscripts. Checks that your references exist, your metadata is accurate, your citations support the claims you make, and your cited papers' own bibliographies are real. Works on LaTeX and PDF. Runs entirely on your machine. Produces a **SciLint Score**.
4
6
 
5
7
  The only open-source tool that combines reference verification, claim checking, manuscript consistency, figure analysis, and bibliography verification in one pipeline — powered entirely by a local LLM on your own GPU.
@@ -103,6 +105,32 @@ uv tool install sciwrite-lint --python 3.13
103
105
 
104
106
  uv downloads Python 3.13 automatically (does not affect any Python you may already have) and installs sciwrite-lint as a globally available command.
105
107
 
108
+ ### Reproducible environment (optional)
109
+
110
+ `pyproject.toml` declares lower bounds for dependencies, so `uv tool install` / `pip install` may resolve newer versions as upstream releases ship. If you want the exact versions the maintainer develops and tests against — for reproducing benchmarks, debugging version-drift issues, or CI — install from [`requirements-pinned.txt`](requirements-pinned.txt) instead.
111
+
112
+ **What's in it:** top-level packages declared in `pyproject.toml` pinned to `==` versions. Transitive dependencies are not pinned — pip resolves them within each top-level package's own constraints. This is a deliberate middle ground: it locks the packages sciwrite-lint actually imports and tests against, without over-constraining the dep tree.
113
+
114
+ ```bash
115
+ # Clone the public repo (required — uv tool install can't read a local file)
116
+ git clone https://github.com/authentic-research-partners/sciwrite-lint.git
117
+ cd sciwrite-lint
118
+
119
+ # With uv (recommended — fast, isolated, manages Python itself)
120
+ uv venv --python 3.13 .venv
121
+ source .venv/bin/activate
122
+ uv pip install -r requirements-pinned.txt
123
+ uv pip install -e . --no-deps # install sciwrite-lint source; deps already pinned
124
+
125
+ # Or with plain pip
126
+ python3.13 -m venv .venv
127
+ source .venv/bin/activate
128
+ pip install -r requirements-pinned.txt
129
+ pip install -e . --no-deps
130
+ ```
131
+
132
+ Activate the venv in each new terminal with `source .venv/bin/activate`. For most users, the plain `uv tool install sciwrite-lint` above is simpler — use the pinned file only when you need exact version reproduction.
133
+
106
134
  ## Setup
107
135
 
108
136
  ```bash
@@ -116,7 +144,18 @@ sciwrite-lint containers monitor # live dashboard: service health, VRAM, K
116
144
 
117
145
  `init` detects `.tex` files and their `.bib` references and generates a `.sciwrite-lint.toml` config. Review to confirm the right files were detected.
118
146
 
119
- **Paywalled references:** drop PDFs into `local_pdfs/` with filenames matching the reference title. The tool fuzzy-matches filenames against your `.bib` titles and uses local copies instead of downloading.
147
+ **Providing PDFs manually (`local_pdfs/`):** Drop any reference PDF into the `local_pdfs/` directory to skip the download step entirely. This is useful when you already have the file, when the paper is paywalled, or when the publisher blocks automated downloads (Cell, Springer, some PMC pages). Name files by the reference title:
148
+
149
+ ```
150
+ local_pdfs/
151
+ ├── States of Curiosity Modulate Hippocampus-Dependent Learning.pdf
152
+ ├── Mind in Society.pdf
153
+ └── The System of Professions.pdf
154
+ ```
155
+
156
+ The tool fuzzy-matches filenames against your `.bib` titles (threshold 0.80) and copies matches to the workspace. On the next run, matched references upgrade from T2 to T1 and go through GROBID parsing and claim verification like any other full-text reference.
157
+
158
+ **Finding what to download:** `sciwrite-lint verify` lists T2 references that are confirmed open access with direct URLs — open in your browser and save to `local_pdfs/`. For paywalled papers, use your institution's library access.
120
159
 
121
160
  **Optional API keys** increase rate limits for Semantic Scholar, NCBI, and CORE:
122
161
 
@@ -218,6 +257,21 @@ For contributors and advanced users:
218
257
  - [docs/evals.md](https://github.com/authentic-research-partners/sciwrite-lint/blob/main/docs/evals.md) — detection evaluation framework, adding test cases
219
258
  - [docs/calibration.md](https://github.com/authentic-research-partners/sciwrite-lint/blob/main/docs/calibration.md) — SciLint Score calibration against ground-truth papers
220
259
 
260
+ ## Citation
261
+
262
+ If you use sciwrite-lint in your research, please cite the accompanying paper ([arXiv:2604.08501](https://arxiv.org/abs/2604.08501)):
263
+
264
+ ```bibtex
265
+ @article{samsonau2026sciwritelint,
266
+ title = {sciwrite-lint: Verification Infrastructure for the Age of Science Vibe-Writing},
267
+ author = {Samsonau, Sergey V.},
268
+ journal = {arXiv preprint arXiv:2604.08501},
269
+ year = {2026},
270
+ }
271
+ ```
272
+
273
+ GitHub's "Cite this repository" button (powered by [`CITATION.cff`](CITATION.cff)) will also generate this for you.
274
+
221
275
  ## License
222
276
 
223
277
  MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sciwrite-lint"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "A linter for scientific manuscripts — reference verification, consistency checking, and structural validation."
9
9
  requires-python = ">=3.13,<3.14"
10
10
  license = "MIT"
@@ -53,6 +53,7 @@ Homepage = "https://arpconnect.com"
53
53
  Repository = "https://github.com/authentic-research-partners/sciwrite-lint"
54
54
  Documentation = "https://github.com/authentic-research-partners/sciwrite-lint#readme"
55
55
  Issues = "https://github.com/authentic-research-partners/sciwrite-lint/issues"
56
+ Paper = "https://arxiv.org/abs/2604.08501"
56
57
 
57
58
  [project.optional-dependencies]
58
59
  dev = [
@@ -1,3 +1,3 @@
1
1
  """sciwrite-lint: a linter for scientific manuscripts."""
2
2
 
3
- __version__ = "0.2.1"
3
+ __version__ = "0.2.2"
@@ -123,6 +123,12 @@ def fetch_for_citations(
123
123
  except Exception as e:
124
124
  logger.warning(f"{c.key}: parse skipped: {e}")
125
125
  elif result.url:
126
+ if result.is_oa:
127
+ meta.access["is_oa"] = True
128
+ if result.oa_url:
129
+ meta.access["oa_url"] = result.oa_url
130
+ if result.is_oa or result.oa_url:
131
+ save_metadata(meta, refs_dir)
126
132
  logger.warning(f"{c.key}: manual download needed: {result.url}")
127
133
  else:
128
134
  logger.warning(f"{c.key}: no source found")
@@ -8,7 +8,32 @@ from pathlib import Path
8
8
 
9
9
  from loguru import logger
10
10
 
11
- from sciwrite_lint.models import CheckResult, Finding
11
+ from sciwrite_lint.models import CheckResult, Citation, CitationMetadata, Finding
12
+
13
+
14
+ def classify_t2_refs(
15
+ citations: list[Citation],
16
+ all_meta: dict[str, CitationMetadata],
17
+ ) -> tuple[list[tuple[str, str]], list[tuple[str, str]]]:
18
+ """Split T2 refs into OA (manual download) and other (no source).
19
+
20
+ Returns (t2_oa, t2_other) where each is a list of (key, url_or_reason).
21
+ """
22
+ t2_oa: list[tuple[str, str]] = []
23
+ t2_other: list[tuple[str, str]] = []
24
+ for c in sorted(citations, key=lambda x: x.key):
25
+ meta = all_meta.get(c.key)
26
+ if not meta:
27
+ continue
28
+ tier = meta.access.get("tier", "NC")
29
+ if tier == "T2":
30
+ if meta.access.get("is_oa") and meta.access.get("oa_url"):
31
+ t2_oa.append((c.key, meta.access["oa_url"]))
32
+ else:
33
+ reason = meta.access.get("acquisition_reason", "")
34
+ if reason:
35
+ t2_other.append((c.key, reason))
36
+ return t2_oa, t2_other
12
37
 
13
38
 
14
39
  _TIER_MARKERS = {
@@ -205,21 +230,18 @@ def run_status(args: argparse.Namespace) -> int:
205
230
  print(" T2 = verified, no full text (confirmed real)")
206
231
  print(" T3 = not found in APIs or dead URL")
207
232
 
208
- # Show acquisition reasons for T2 refs
209
- t2_reasons: list[tuple[str, str]] = []
210
- for c in sorted(citations, key=lambda x: x.key):
211
- meta = all_meta.get(c.key)
212
- if not meta:
213
- continue
214
- tier = meta.access.get("tier", "NC")
215
- if tier == "T2":
216
- reason = meta.access.get("acquisition_reason", "")
217
- if reason:
218
- t2_reasons.append((c.key, reason))
219
- if t2_reasons:
233
+ # Show T2 refs split by OA status
234
+ t2_oa, t2_other = classify_t2_refs(citations, all_meta)
235
+ if t2_oa:
236
+ print()
237
+ print(" T2 open access — manual download needed:")
238
+ print(" (download PDF, save to local_pdfs/ by title)")
239
+ for key, url in t2_oa:
240
+ print(f" {key}: {url}")
241
+ if t2_other:
220
242
  print()
221
243
  print(" T2 acquisition details:")
222
- for key, reason in t2_reasons:
244
+ for key, reason in t2_other:
223
245
  print(f" {key}: {reason}")
224
246
 
225
247
  return 0
@@ -122,6 +122,8 @@ class AcquisitionResult(BaseModel):
122
122
  url: str | None = None
123
123
  abstract: str | None = None
124
124
  reason: str = ""
125
+ is_oa: bool = False
126
+ oa_url: str | None = None
125
127
 
126
128
 
127
129
  # ---------------------------------------------------------------------------
@@ -788,10 +790,18 @@ async def acquire_fulltext(
788
790
  if progress:
789
791
  print(" not found")
790
792
 
791
- # 9. Suggest manual download
793
+ # 9. Determine OA status and suggest manual download
792
794
  combined_reason = (
793
795
  "; ".join(failed_sources) if failed_sources else "no sources available"
794
796
  )
797
+ # OA confirmed by Unpaywall or OpenAlex
798
+ known_oa = bool(uw_data) or bool(oa_url)
799
+ best_oa_url = None
800
+ if uw_data:
801
+ best_oa_url = uw_data.get("oa_url") or uw_data.get("pdf_url")
802
+ if not best_oa_url and oa_url:
803
+ best_oa_url = oa_url
804
+
795
805
  best_url = None
796
806
  if uw_data:
797
807
  best_url = uw_data.get("pdf_url") or uw_data.get("oa_url")
@@ -806,6 +816,8 @@ async def acquire_fulltext(
806
816
  url=best_url,
807
817
  abstract=core_abstract,
808
818
  reason=combined_reason,
819
+ is_oa=known_oa,
820
+ oa_url=best_oa_url,
809
821
  )
810
822
 
811
823
  return AcquisitionResult(
@@ -1548,6 +1548,11 @@ async def _stage_fetch(
1548
1548
  elif not result.found:
1549
1549
  if result.reason:
1550
1550
  meta.access["acquisition_reason"] = result.reason
1551
+ if result.is_oa:
1552
+ meta.access["is_oa"] = True
1553
+ if result.oa_url:
1554
+ meta.access["oa_url"] = result.oa_url
1555
+ if result.reason or result.is_oa or result.oa_url:
1551
1556
  save_metadata(meta, references_dir)
1552
1557
  logger.debug(
1553
1558
  f"{c.key}: PDF not acquired (stays T2)"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sciwrite-lint
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: A linter for scientific manuscripts — reference verification, consistency checking, and structural validation.
5
5
  Author: Sergey Samsonau
6
6
  Maintainer-email: Authentic Research Partners LLC <info@arpconnect.com>
@@ -9,6 +9,7 @@ Project-URL: Homepage, https://arpconnect.com
9
9
  Project-URL: Repository, https://github.com/authentic-research-partners/sciwrite-lint
10
10
  Project-URL: Documentation, https://github.com/authentic-research-partners/sciwrite-lint#readme
11
11
  Project-URL: Issues, https://github.com/authentic-research-partners/sciwrite-lint/issues
12
+ Project-URL: Paper, https://arxiv.org/abs/2604.08501
12
13
  Keywords: linter,latex,bibtex,pdf,scientific-writing,academic-writing,citation-checker,reference-verification,claim-verification,manuscript,peer-review,arxiv,vllm,qwen
13
14
  Classifier: Development Status :: 3 - Alpha
14
15
  Classifier: Intended Audience :: Developers
@@ -51,6 +52,8 @@ Dynamic: license-file
51
52
 
52
53
  # sciwrite-lint
53
54
 
55
+ [![arXiv](https://img.shields.io/badge/arXiv-2604.08501-b31b1b.svg)](https://arxiv.org/abs/2604.08501)
56
+
54
57
  A linter for scientific manuscripts. Checks that your references exist, your metadata is accurate, your citations support the claims you make, and your cited papers' own bibliographies are real. Works on LaTeX and PDF. Runs entirely on your machine. Produces a **SciLint Score**.
55
58
 
56
59
  The only open-source tool that combines reference verification, claim checking, manuscript consistency, figure analysis, and bibliography verification in one pipeline — powered entirely by a local LLM on your own GPU.
@@ -154,6 +157,32 @@ uv tool install sciwrite-lint --python 3.13
154
157
 
155
158
  uv downloads Python 3.13 automatically (does not affect any Python you may already have) and installs sciwrite-lint as a globally available command.
156
159
 
160
+ ### Reproducible environment (optional)
161
+
162
+ `pyproject.toml` declares lower bounds for dependencies, so `uv tool install` / `pip install` may resolve newer versions as upstream releases ship. If you want the exact versions the maintainer develops and tests against — for reproducing benchmarks, debugging version-drift issues, or CI — install from [`requirements-pinned.txt`](requirements-pinned.txt) instead.
163
+
164
+ **What's in it:** top-level packages declared in `pyproject.toml` pinned to `==` versions. Transitive dependencies are not pinned — pip resolves them within each top-level package's own constraints. This is a deliberate middle ground: it locks the packages sciwrite-lint actually imports and tests against, without over-constraining the dep tree.
165
+
166
+ ```bash
167
+ # Clone the public repo (required — uv tool install can't read a local file)
168
+ git clone https://github.com/authentic-research-partners/sciwrite-lint.git
169
+ cd sciwrite-lint
170
+
171
+ # With uv (recommended — fast, isolated, manages Python itself)
172
+ uv venv --python 3.13 .venv
173
+ source .venv/bin/activate
174
+ uv pip install -r requirements-pinned.txt
175
+ uv pip install -e . --no-deps # install sciwrite-lint source; deps already pinned
176
+
177
+ # Or with plain pip
178
+ python3.13 -m venv .venv
179
+ source .venv/bin/activate
180
+ pip install -r requirements-pinned.txt
181
+ pip install -e . --no-deps
182
+ ```
183
+
184
+ Activate the venv in each new terminal with `source .venv/bin/activate`. For most users, the plain `uv tool install sciwrite-lint` above is simpler — use the pinned file only when you need exact version reproduction.
185
+
157
186
  ## Setup
158
187
 
159
188
  ```bash
@@ -167,7 +196,18 @@ sciwrite-lint containers monitor # live dashboard: service health, VRAM, K
167
196
 
168
197
  `init` detects `.tex` files and their `.bib` references and generates a `.sciwrite-lint.toml` config. Review to confirm the right files were detected.
169
198
 
170
- **Paywalled references:** drop PDFs into `local_pdfs/` with filenames matching the reference title. The tool fuzzy-matches filenames against your `.bib` titles and uses local copies instead of downloading.
199
+ **Providing PDFs manually (`local_pdfs/`):** Drop any reference PDF into the `local_pdfs/` directory to skip the download step entirely. This is useful when you already have the file, when the paper is paywalled, or when the publisher blocks automated downloads (Cell, Springer, some PMC pages). Name files by the reference title:
200
+
201
+ ```
202
+ local_pdfs/
203
+ ├── States of Curiosity Modulate Hippocampus-Dependent Learning.pdf
204
+ ├── Mind in Society.pdf
205
+ └── The System of Professions.pdf
206
+ ```
207
+
208
+ The tool fuzzy-matches filenames against your `.bib` titles (threshold 0.80) and copies matches to the workspace. On the next run, matched references upgrade from T2 to T1 and go through GROBID parsing and claim verification like any other full-text reference.
209
+
210
+ **Finding what to download:** `sciwrite-lint verify` lists T2 references that are confirmed open access with direct URLs — open in your browser and save to `local_pdfs/`. For paywalled papers, use your institution's library access.
171
211
 
172
212
  **Optional API keys** increase rate limits for Semantic Scholar, NCBI, and CORE:
173
213
 
@@ -269,6 +309,21 @@ For contributors and advanced users:
269
309
  - [docs/evals.md](https://github.com/authentic-research-partners/sciwrite-lint/blob/main/docs/evals.md) — detection evaluation framework, adding test cases
270
310
  - [docs/calibration.md](https://github.com/authentic-research-partners/sciwrite-lint/blob/main/docs/calibration.md) — SciLint Score calibration against ground-truth papers
271
311
 
312
+ ## Citation
313
+
314
+ If you use sciwrite-lint in your research, please cite the accompanying paper ([arXiv:2604.08501](https://arxiv.org/abs/2604.08501)):
315
+
316
+ ```bibtex
317
+ @article{samsonau2026sciwritelint,
318
+ title = {sciwrite-lint: Verification Infrastructure for the Age of Science Vibe-Writing},
319
+ author = {Samsonau, Sergey V.},
320
+ journal = {arXiv preprint arXiv:2604.08501},
321
+ year = {2026},
322
+ }
323
+ ```
324
+
325
+ GitHub's "Cite this repository" button (powered by [`CITATION.cff`](CITATION.cff)) will also generate this for you.
326
+
272
327
  ## License
273
328
 
274
329
  MIT
File without changes
File without changes