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.
- {sciwrite_lint-0.2.1/sciwrite_lint.egg-info → sciwrite_lint-0.2.2}/PKG-INFO +57 -2
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/README.md +55 -1
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/pyproject.toml +2 -1
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/__init__.py +1 -1
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cli/fetch.py +6 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cli/verify.py +36 -14
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/fulltext.py +13 -1
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/pipeline.py +5 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2/sciwrite_lint.egg-info}/PKG-INFO +57 -2
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/LICENSE +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/__main__.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/_network.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/api.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/__init__.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/_section_utils.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/cite_purpose.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/claim_support.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/cross_section_consistency.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/dangling_cite.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/dangling_ref.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/full_paper_consistency.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/ref_internal_checks.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/reference_accuracy.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/reference_exists.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/reference_unreliable.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/registry.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/retracted_cite.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/structure_promises.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/unreferenced_figure.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/claims.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/claude_backend.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/claude_cli.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cli/__init__.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cli/check.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cli/config.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cli/misc.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cli/rank.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/config.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/cross_paper.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/eval_claims.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/llm_utils.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/local_pdfs.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/manuscript_store.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/models.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/pdf/__init__.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/pdf/grobid.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/pdf/pdf_download.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/prompt_safety.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/py.typed +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/rate_limiter.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/__init__.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/citations.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/crossref.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/embedding_store.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/matching.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/metadata.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/reference_store.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/retraction_watch.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/references/workspace_db.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/report.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/schemas.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/scoring/__init__.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/scoring/chain.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/scoring/contribution.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/scoring/scilint_score.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/tex_parser.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/usage.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vision/__init__.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vision/cache.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vision/describe.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vision/image_extraction.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vision/pipeline.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vllm/__init__.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vllm/metrics.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/vllm/vllm_server.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/web.py +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint.egg-info/SOURCES.txt +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint.egg-info/dependency_links.txt +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint.egg-info/entry_points.txt +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint.egg-info/requires.txt +0 -0
- {sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint.egg-info/top_level.txt +0 -0
- {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.
|
|
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
|
+
[](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
|
-
**
|
|
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
|
+
[](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
|
-
**
|
|
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.
|
|
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 = [
|
|
@@ -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
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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
|
+
[](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
|
-
**
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sciwrite_lint-0.2.1 → sciwrite_lint-0.2.2}/sciwrite_lint/checks/cross_section_consistency.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|