paperlint 2.1.0 → 3.0.0

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 (191) hide show
  1. package/CLAUDE.md +6 -1
  2. package/README.md +233 -224
  3. package/action.yml +4 -6
  4. package/dist/adapters/references/index.d.ts +13 -0
  5. package/dist/adapters/references/index.d.ts.map +1 -0
  6. package/dist/adapters/references/index.js +55 -0
  7. package/dist/adapters/references/index.js.map +1 -0
  8. package/dist/adapters/references/reach.io.d.ts +3 -0
  9. package/dist/adapters/references/reach.io.d.ts.map +1 -0
  10. package/dist/adapters/references/reach.io.js +14 -0
  11. package/dist/adapters/references/reach.io.js.map +1 -0
  12. package/dist/build.d.ts +15 -0
  13. package/dist/build.d.ts.map +1 -1
  14. package/dist/build.js +38 -4
  15. package/dist/build.js.map +1 -1
  16. package/dist/cli.d.ts +28 -25
  17. package/dist/cli.d.ts.map +1 -1
  18. package/dist/cli.js +277 -265
  19. package/dist/cli.js.map +1 -1
  20. package/dist/doctor.d.ts.map +1 -1
  21. package/dist/doctor.js +40 -72
  22. package/dist/doctor.js.map +1 -1
  23. package/dist/facts-file.d.ts +2 -2
  24. package/dist/facts-file.js +2 -2
  25. package/dist/hooks-settings.d.ts +0 -21
  26. package/dist/hooks-settings.d.ts.map +1 -1
  27. package/dist/hooks-settings.js +17 -135
  28. package/dist/hooks-settings.js.map +1 -1
  29. package/dist/init.d.ts +39 -29
  30. package/dist/init.d.ts.map +1 -1
  31. package/dist/init.js +143 -172
  32. package/dist/init.js.map +1 -1
  33. package/dist/link-skills.d.ts +1 -1
  34. package/dist/link-skills.d.ts.map +1 -1
  35. package/dist/link-skills.js +11 -29
  36. package/dist/link-skills.js.map +1 -1
  37. package/dist/new-paper.d.ts.map +1 -1
  38. package/dist/new-paper.js +2 -13
  39. package/dist/new-paper.js.map +1 -1
  40. package/dist/paper-files.d.ts +63 -0
  41. package/dist/paper-files.d.ts.map +1 -0
  42. package/dist/paper-files.js +69 -0
  43. package/dist/paper-files.js.map +1 -0
  44. package/dist/paper-settings.d.ts +13 -41
  45. package/dist/paper-settings.d.ts.map +1 -1
  46. package/dist/paper-settings.js +71 -71
  47. package/dist/paper-settings.js.map +1 -1
  48. package/dist/ports/check-references.d.ts +31 -0
  49. package/dist/ports/check-references.d.ts.map +1 -0
  50. package/dist/ports/check-references.js +7 -0
  51. package/dist/ports/check-references.js.map +1 -0
  52. package/dist/presets.d.ts +1 -1
  53. package/dist/presets.d.ts.map +1 -1
  54. package/dist/presets.js +4 -6
  55. package/dist/presets.js.map +1 -1
  56. package/dist/reference-rules.d.ts +47 -0
  57. package/dist/reference-rules.d.ts.map +1 -0
  58. package/dist/reference-rules.js +156 -0
  59. package/dist/reference-rules.js.map +1 -0
  60. package/dist/references.d.ts +36 -0
  61. package/dist/references.d.ts.map +1 -0
  62. package/dist/references.js +91 -0
  63. package/dist/references.js.map +1 -0
  64. package/dist/rules-config.d.ts +5 -3
  65. package/dist/rules-config.d.ts.map +1 -1
  66. package/dist/rules-config.js +15 -6
  67. package/dist/rules-config.js.map +1 -1
  68. package/dist/structure.js +3 -3
  69. package/dist/structure.js.map +1 -1
  70. package/dist/tex-requirements.js +2 -2
  71. package/dist/tex-requirements.js.map +1 -1
  72. package/dist/types.d.ts +20 -18
  73. package/dist/types.d.ts.map +1 -1
  74. package/dist/venue-rules.d.ts.map +1 -1
  75. package/dist/venue-rules.js +11 -9
  76. package/dist/venue-rules.js.map +1 -1
  77. package/docs/configuration.md +119 -104
  78. package/docs/e2e.md +1 -1
  79. package/docs/install.md +25 -40
  80. package/docs/optional-rules.md +5 -4
  81. package/docs/rules.md +60 -23
  82. package/eslint-rules/bib-reachable-entry.mjs +75 -0
  83. package/eslint-rules/latex-language.mjs +29 -1
  84. package/eslint-rules/paper-stages.harness.mjs +1 -117
  85. package/eslint-rules/paper-stages.mjs +0 -120
  86. package/eslint-rules/paper-stages.mutations.mjs +0 -76
  87. package/eslint-rules/paper-typography.mjs +267 -192
  88. package/eslint-rules/paper-typography.test.mjs +297 -0
  89. package/eslint-rules/papers.harness.mjs +8 -12
  90. package/eslint-rules/papers.mjs +17 -31
  91. package/eslint-rules/pdf-last-page-balance.mjs +4 -7
  92. package/eslint-rules/review-frontmatter.mjs +118 -0
  93. package/eslint-rules/review-frontmatter.schema.json +36 -0
  94. package/eslint-rules/review-frontmatter.test.mjs +118 -0
  95. package/eslint-rules/sibling-frontmatter.mjs +19 -0
  96. package/eslint-rules/sibling-frontmatter.schema.json +14 -0
  97. package/eslint.config.mjs +0 -15
  98. package/fixtures/real-markdown-paper/baseline.json +6 -7
  99. package/hooks/hooks.harness.mjs +39 -98
  100. package/hooks/hooks.mutations.mjs +6 -6
  101. package/hooks/paper-edit-guard.hook.d.mts +2 -3
  102. package/hooks/paper-edit-guard.hook.mjs +31 -42
  103. package/hooks/paper-skills-nudge.hook.mjs +21 -19
  104. package/hooks/paper-status-gates.hook.mjs +21 -19
  105. package/hooks/paper-status-gates.sh +8 -13
  106. package/lib/paper-config.d.mts +15 -25
  107. package/lib/paper-config.harness.mjs +18 -35
  108. package/lib/paper-config.mjs +83 -105
  109. package/lib/paper-config.mutations.mjs +2 -2
  110. package/lib/skill-checks.mjs +1 -1
  111. package/lib/skill-trigger-cases.harness.mjs +9 -8
  112. package/lib/skill-trigger-cases.mjs +7 -15
  113. package/package.json +1 -1
  114. package/scripts/eslint-report-guard.mjs +1 -1
  115. package/scripts/harness-api.frozen.json +0 -3
  116. package/scripts/mutation-batteries.frozen.json +4 -13
  117. package/skills/analyze-sibling-paper/SKILL.md +16 -12
  118. package/skills/analyze-sibling-paper/SKILL.md.spec.ts +15 -11
  119. package/skills/cold-read-diff/SKILL.md +20 -1
  120. package/skills/cold-read-diff/SKILL.md.spec.ts +19 -0
  121. package/skills/grade-paper-writing/SKILL.md +20 -1
  122. package/skills/grade-paper-writing/SKILL.md.spec.ts +19 -0
  123. package/skills/paper-pipeline/scripts/consumer.d.mts +0 -1
  124. package/skills/paper-pipeline/scripts/consumer.harness.mjs +8 -12
  125. package/skills/paper-pipeline/scripts/consumer.mjs +19 -33
  126. package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +6 -4
  127. package/skills/paper-pipeline/scripts/run-mechanical.mjs +2 -11
  128. package/skills/plan-paper-timeline/SKILL.md +2 -2
  129. package/skills/plan-paper-timeline/SKILL.md.spec.ts +1 -1
  130. package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +1 -1
  131. package/skills/plan-paper-timeline/plan-paper-timeline.effects.mutations.mjs +2 -2
  132. package/skills/render-paper/check-render.sh +2 -3
  133. package/skills/render-paper/render-paper.harness.mjs +2 -2
  134. package/skills/verify-citations/SKILL.md +10 -1
  135. package/skills/verify-citations/SKILL.md.spec.ts +9 -0
  136. package/skills/verify-citations/scripts/bib-authors.mjs +48 -26
  137. package/src/adapters/references/index.ts +102 -0
  138. package/src/adapters/references/reach.io.ts +12 -0
  139. package/src/build.harness.mjs +8 -4
  140. package/src/build.ts +62 -6
  141. package/src/cli.harness.mjs +147 -292
  142. package/src/cli.mutations.mjs +35 -44
  143. package/src/cli.ts +344 -304
  144. package/src/doctor.harness.mjs +41 -84
  145. package/src/doctor.mutations.mjs +9 -9
  146. package/src/doctor.ts +44 -89
  147. package/src/facts-file.test.ts +0 -7
  148. package/src/facts-file.ts +2 -2
  149. package/src/hooks-settings.harness.mjs +7 -92
  150. package/src/hooks-settings.mutations.mjs +2 -17
  151. package/src/hooks-settings.ts +18 -162
  152. package/src/init.test.ts +206 -0
  153. package/src/init.ts +186 -230
  154. package/src/link-skills.harness.mjs +1 -38
  155. package/src/link-skills.mutations.mjs +9 -5
  156. package/src/link-skills.ts +10 -34
  157. package/src/lint-fix.test.ts +120 -0
  158. package/src/new-paper.test.ts +1 -13
  159. package/src/new-paper.ts +2 -17
  160. package/src/paper-files.test.ts +290 -0
  161. package/src/paper-files.ts +144 -0
  162. package/src/paper-settings-commands.test.ts +84 -121
  163. package/src/paper-settings.test.ts +93 -64
  164. package/src/paper-settings.ts +99 -97
  165. package/src/ports/check-references.ts +28 -0
  166. package/src/presets.ts +5 -15
  167. package/src/reference-rules.ts +229 -0
  168. package/src/references.test.ts +209 -0
  169. package/src/references.ts +138 -0
  170. package/src/rules-config.harness.mjs +24 -8
  171. package/src/rules-config.ts +19 -7
  172. package/src/structure.harness.mjs +3 -1
  173. package/src/structure.ts +3 -3
  174. package/src/tex-requirements.ts +2 -2
  175. package/src/types.ts +30 -18
  176. package/src/venue-rules.test.ts +20 -11
  177. package/src/venue-rules.ts +11 -13
  178. package/eslint-rules/doc-fields.harness.mjs +0 -336
  179. package/eslint-rules/doc-fields.mjs +0 -186
  180. package/eslint-rules/doc-fields.mutations.mjs +0 -96
  181. package/eslint-rules/paper-typography.harness.mjs +0 -291
  182. package/eslint-rules/paper-typography.mutations.mjs +0 -131
  183. package/eslint-rules/review-findings-cause.harness.mjs +0 -228
  184. package/eslint-rules/review-findings-cause.mjs +0 -135
  185. package/eslint-rules/review-findings-cause.mutations.mjs +0 -72
  186. package/fixtures/paper-stages/authors-ran/PIPELINE-STATUS.md +0 -16
  187. package/fixtures/paper-stages/marker-in-prose/PIPELINE-STATUS.md +0 -17
  188. package/fixtures/review-findings-cause/clean.md +0 -17
  189. package/fixtures/review-findings-cause/defect.md +0 -14
  190. package/fixtures/review-findings-cause/old-debt.md +0 -14
  191. package/fixtures/review-findings-cause/quiet-in-fence.md +0 -16
package/CLAUDE.md CHANGED
@@ -193,7 +193,7 @@ the CONFIG is not declaring something. Check that before adding a verb.
193
193
 
194
194
  `npm i -D paperlint` brings all the code — rules, skills, hooks, scripts. `paperlint init`
195
195
  then does what only a command can, because it depends on the project it lands in: it finds the
196
- papers directory and declares it in `package.json`, links each skill into `.claude/skills/`, writes
196
+ papers directory and declares it in a root `paperlint.json` when it is not the default `papers`, links each skill into `.claude/skills/`, writes
197
197
  the hook commands into `.claude/settings.json` (vigiles' `mergeRegistrations`, reading
198
198
  `plugin/hooks/hooks.json` as the one source), and offers a CI workflow pinned to the installed
199
199
  release's tag. `paperlint doctor` reads all of it back. Details: `docs/install.md`.
@@ -520,6 +520,11 @@ import none of the three are frozen in `scripts/harness-api.frozen.json`, which
520
520
  exits 1 when no file matches, and it transpiles without type-checking, so `npm run check` runs
521
521
  `tsc -p tsconfig.test.json` as its own gate.
522
522
 
523
+ **Local = the fast gates on what you touched; the full `npm run check` = CI.** Locally run vitest on
524
+ the touched test files, `npx tsc --noEmit`, `npx eslint <touched files>` and `npm run fmt:check`, then
525
+ push and read CI by job name. The repo is public, so CI is free, and it also runs the TeX e2e that
526
+ cannot run locally; a local full run takes 10-15 min, most of it the mutation batteries (#52).
527
+
523
528
  ⚠️ **Not `vigiles test .`** — the `.` is read as a FILE, the runner dies with
524
529
  `ERR_UNSUPPORTED_DIR_IMPORT`, and it still exits 0. See the measured table below.
525
530
 
package/README.md CHANGED
@@ -1,297 +1,306 @@
1
+ <!--
2
+ README style — keep it scannable, not a wall of text:
3
+ - One idea per paragraph; paragraphs ≤ 3 lines. Split anything longer.
4
+ - Every section starts with a heading (##); sub-topics get ###. No section longer than one screen.
5
+ - Lists over prose for anything with 3+ items; bullets ≤ 2 lines.
6
+ - Tables for comparisons (commands, venues, checks). Code blocks for every command and file.
7
+ - A blank line between every block. Bold only for the one phrase a skimmer must see.
8
+ - Mechanics and edge cases live in docs/, linked — not inline.
9
+ - A reader must be able to answer "what is it, which venues, how do I start" from the first screen.
10
+ - Sparse emoji are fine where they help rhythm and scanning (e.g. one per section heading or feature bullet); never decorative, never several in a row.
11
+ -->
12
+
1
13
  # paperlint
2
14
 
3
15
  [![npm version](https://img.shields.io/npm/v/paperlint)](https://www.npmjs.com/package/paperlint)
4
16
  ![Node version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fzernie%2Fpaperlint%2Fmain%2Fpackage.json&query=%24.engines.node&label=node)
5
17
 
6
- A linter for scientific papers written in LaTeX. It catches the mechanical mistakes that get a
18
+ **A linter for scientific papers written in LaTeX.** It catches the mechanical mistakes that get a
7
19
  paper desk-rejected or sent back at camera-ready, before you submit.
8
20
 
9
- A LaTeX paper can build without an error and still be wrong. The ACM template silently switches to
10
- another font when one of its font packages is missing, and the pages break differently. The same
11
- source paginates differently on a machine with another version of the template. The submitted PDF
12
- gets overwritten a week later and nobody can say what was sent. You find out from a reviewer or a
13
- publisher, or never. paperlint checks for these on your machine and in CI:
14
-
15
- - **Checks against your venue.** Point the paper's `paperlint.json` at a venue preset
16
- (`"extends": "paperlint:aisec"`), build, lint: the page limit for your kind of paper, embedded
17
- fonts and the template's font families, the paper size and column count, and the font sizes, as
18
- the venue's call for papers sets them — plus the checks the venue implies, such as a balanced last
19
- page. Presets ship for the ACM `acmart` sigconf family and three venues — AgenticDev and AISec
20
- (ACM) and REALM (ACL); for any other venue, a preset is one JSON file in your repository
21
- ([`docs/rules.md`](docs/rules.md#writing-your-own-venue-preset)).
22
- - **Builds the same PDF everywhere.** `paperlint toolchain` installs TeX Live with exactly the
23
- packages your venue's template needs and checks that each one is really there, so the silent
24
- font switch cannot happen and your laptop and CI build with the same TeX Live.
25
- - **Keeps what you submitted.** Record "submitted on 22 July, as this PDF", and paperlint fails if
26
- that PDF changes or disappears, or its LaTeX source was not kept beside it.
27
- - **Knows the classic slips.** An ACM paper that overrides the title-page commands and loses part
28
- of page 1; "code will be released" left in a camera-ready; references without a DOI or URL; an
29
- unbalanced last page, for publishers that ask for balanced columns (an optional check).
30
- - **Works with coding agents.** Hooks and skills for Claude Code run the checks after every edit
31
- and stop an agent from rewriting the paper around them.
32
-
33
- It is for researchers and engineers who write papers in LaTeX inside git and submit them to
34
- conferences or journals.
35
-
36
- ## Install and set up
21
+ A LaTeX paper can build without an error and still be wrong: a missing font package silently
22
+ changes the font, another template version changes the pagination, the submitted PDF gets
23
+ overwritten. paperlint checks for these on your machine and in CI:
24
+
25
+ - 📏 **Checks against your venue.** Page limit, embedded fonts, paper size, columns and font sizes,
26
+ as the call for papers sets them.
27
+ - 🧱 **Builds the same PDF everywhere.** `paperlint toolchain` installs TeX Live with exactly the
28
+ packages your venue's template needs.
29
+ - 📌 **Keeps what you submitted.** Record the PDF you sent, and paperlint fails if it changes or
30
+ disappears, or its LaTeX source was not kept.
31
+ - 🔗 **Checks your references online.** At build time: each cited work exists, and its author list
32
+ is the published version's.
33
+ - ✏️ **Knows the classic slips.** `§` instead of "Section", `.05` instead of `0.05`.
34
+ `paperlint lint --fix` fixes them.
35
+
36
+ It is built on ESLint: the checks are ESLint rules over `.tex` and `.md` files, named like
37
+ `paper/leading-zero`. You do not need to know ESLint to use it.
38
+
39
+ ## Contents
40
+
41
+ - [Supported venues](#-supported-venues)
42
+ - [Skills](#-skills)
43
+ - [Getting started](#-getting-started)
44
+ - [Commands](#-commands)
45
+ - [Configuration](#-configuration)
46
+ - [Run it in CI](#-run-it-in-ci)
47
+ - [FAQ](#-faq)
48
+ - [Docs](#-docs)
49
+
50
+ ## 🎯 Supported venues
51
+
52
+ A preset holds a venue's format and page limits. Its **kind** is the paper type the venue sets a
53
+ limit for — `short`, `full`, `research` — named once per paper in its `paperlint.json`.
54
+
55
+ | preset | venue | format | page limit |
56
+ | ----------------------- | ------------------ | -------------------------------- | -------------------------------------------------------------- |
57
+ | `paperlint:acm-sigconf` | any ACM conference | ACM two-column conference format | not checked: the family sets no kinds |
58
+ | `paperlint:agenticdev` | AgenticDev @ ASE | ACM two-column conference format | `short` 5, `full` 10, `demo` 5 body pages, + 2 of references |
59
+ | `paperlint:aisec` | AISec @ ACM CCS | ACM two-column conference format | 10 body pages + 2 (`research`, `benchmark`, `position`, `sok`) |
60
+ | `paperlint:realm` | REALM @ EMNLP | ACL two-column format, A4 | `long` 8, `short` 4 — recorded, not checked (below) |
61
+
62
+ REALM's limit is recorded but not checked because ACL leaves the Limitations and Ethics sections
63
+ out of the page count, and the PDF measurement counts them as body pages: a paper within the limit
64
+ would fail.
65
+
66
+ Another venue: [a four-line preset file](#another-venue).
67
+
68
+ ## 🧠 Skills
69
+
70
+ paperlint has two halves:
71
+
72
+ 1. **The linter** — `lint` and `build` — is deterministic: the same paper gives the same verdict,
73
+ so it belongs in CI.
74
+ 2. **The skills** help with the work itself, where the answer is a judgment call. They are
75
+ optional; `paperlint init` installs them for [Claude Code](https://claude.com/claude-code).
76
+
77
+ - **Start here** — run the whole process step by step (`paper-pipeline`), or ask where the paper
78
+ stands (`paper-status`).
79
+ - **Judge the idea** — is it worth doing (`research-ideate`), who else works on it
80
+ (`map-prior-work`), read a close competitor in depth (`analyze-sibling-paper`).
81
+ - **Pick a venue** — find where to send it (`find-venue`), see what gets accepted there
82
+ (`study-accepted-papers`), put the deadlines in your calendar (`plan-paper-timeline`).
83
+ - **Do the study and write** — design the study and its artifact (`build-benchmark`), write the
84
+ draft (`draft-paper`), fix the outline (`argument-arc`), cut it down (`tighten-paper`).
85
+ - **Review before you submit** — a reader with no context (`cold-read-diff`), the prose
86
+ (`grade-paper-writing`), a hostile reviewer (`paper-adversarial-review`), a simulated committee
87
+ (`pc-panel-review`), the references (`verify-citations`), the final check (`harden-paper`).
88
+ - **Submit and after** — render pages to images (`render-paper`), submit (`submit-paper`), upload
89
+ the artifact (`osf-artifact-upload`), camera-ready (`camera-ready`), the next paper (`extend-paper`).
90
+
91
+ ## 🚀 Getting started
37
92
 
38
93
  <!-- `vigiles:symbol src/init.ts#init` — `npm run check` fails if this function is renamed or removed. -->
39
94
 
40
- ```sh
41
- npm i -D paperlint
42
- npx paperlint init
43
- ```
95
+ 1. Install (Node 22.13 or newer):
44
96
 
45
- Node 22.13 or newer. Install first: `paperlint init` links the skills and hooks to the copy in
46
- your project's `node_modules`, so run without the install it has nothing to link to.
97
+ ```sh
98
+ npm i -D paperlint
99
+ ```
47
100
 
48
- `paperlint init` finds your papers directory and records it in `package.json`, offers a CI workflow and a
49
- first paper, and sets up the optional Claude Code skills and hooks. It installs no software. It
50
- asks questions only when you run it in a terminal; an agent, CI or `--yes` gets the defaults, and
51
- each default it takes is printed. It ends by running `paperlint doctor`, which checks the setup.
52
- Exactly what it writes: [`docs/install.md`](docs/install.md#what-paperlint-init-writes).
101
+ 2. Set up — it finds your papers directory, offers a CI workflow and a first paper, and (with
102
+ Claude Code) links the skills:
53
103
 
54
- ## What you get
104
+ ```sh
105
+ npx paperlint init
106
+ ```
55
107
 
56
- | command | what it does |
57
- | ----------------------------- | -------------------------------------------------------------------------------------------------- |
58
- | `npx paperlint init` | sets the project up (see above) |
59
- | `npx paperlint new my-paper` | creates a paper folder from a template; never overwrites a file |
60
- | `npx paperlint lint` | runs every check over your papers; `npx paperlint lint papers/my-paper` checks one |
61
- | `npx paperlint build <paper>` | compiles `paper.tex` to `paper.pdf` with pdflatex and bibtex; `--all` builds every paper |
62
- | `npx paperlint toolchain` | installs TeX Live with the LaTeX packages your venues need; `--check` only reports what is missing |
63
- | `npx paperlint doctor` | checks the setup and exits non-zero if something is miswired |
64
- | `npx paperlint --help` | every command and flag |
108
+ 3. TeX Live, for building — once. `init` offers it; `paperlint lint` alone needs no TeX:
65
109
 
66
- A venue is the conference or journal you submit to. TeX Live is the standard LaTeX distribution;
67
- `paperlint build` offers to install it the first time it needs it.
110
+ ```sh
111
+ npx paperlint toolchain # ~270 MB, ~3 min, once
112
+ ```
68
113
 
69
- ## Your first paper in five minutes
114
+ 4. Create a paper:
70
115
 
71
- ### Starting a paper
116
+ <!-- `vigiles:symbol src/new-paper.ts#newPaper` — `npm run check` fails if this function is renamed or removed. -->
72
117
 
73
- <!-- `vigiles:symbol src/new-paper.ts#newPaper` — `npm run check` fails if this function is renamed or removed. -->
118
+ ```sh
119
+ npx paperlint new my-paper
120
+ ```
74
121
 
75
- After `npx paperlint init`:
122
+ ```
123
+ ✓ created papers/my-paper
124
+ + PIPELINE-STATUS.md (from the package template)
125
+ + paper.tex (from the package template)
126
+ + paperlint.json (from the package template)
127
+ ```
76
128
 
77
- ```sh
78
- npx paperlint new my-paper
79
- ```
129
+ 5. Pick the venue ([the table above](#-supported-venues)) in `papers/my-paper/paperlint.json`:
80
130
 
81
- ```
82
- ✓ created papers/my-paper
83
- + PIPELINE-STATUS.md (from the package template)
84
- + paper.tex (from the package template)
85
- + paperlint.json (from the package template)
131
+ ```json
132
+ { "extends": "paperlint:agenticdev", "kind": "short" }
133
+ ```
86
134
 
87
- config: package.json
88
- …/papers/my-paper/paper.tex
89
- 1:1 warning this paper names no venue preset yet, so its page limit, fonts and format are not checked — set "extends" in papers/my-paper/paperlint.json (e.g. "paperlint:agenticdev"; see docs/rules.md) pdf/measured
135
+ 6. Build the PDF:
90
136
 
91
- ✖ 1 problem (0 errors, 1 warning)
92
- ```
137
+ ```sh
138
+ npx paperlint build papers/my-paper
139
+ ```
93
140
 
94
- The warning is the only thing left to do: the paper does not say where it is going yet.
141
+ 7. Check every paper:
95
142
 
96
- You now have one folder per paper:
143
+ ```sh
144
+ npx paperlint lint
145
+ ```
97
146
 
98
- ```
99
- papers/
100
- my-paper/
101
- paper.tex the paper, in LaTeX
102
- PIPELINE-STATUS.md the paper's record: its research question and the stages it reached
103
- paperlint.json this paper's settings: its venue preset and kind — `new` writes it
104
- reviews/*.md review notes (optional)
105
- versions/ the exact PDF and source you sent at each stage, never edited
106
- ```
147
+ Sample output, on a paper with two slips and no `PIPELINE-STATUS.md`:
107
148
 
108
- `paperlint new` writes `paperlint.json` with `"extends": null` and a comment saying what goes
109
- there. To have the paper checked against its venue, name the venue preset and the kind of paper:
149
+ ```
150
+ papers/hand
151
+ error missing `PIPELINE-STATUS.md` — `paper/stages`, `paper/source` and `paper/research-question` read this file, so nothing `hand` declares about its stages, sources or research question is checked
152
+ …/papers/hand/paper.tex
153
+ 3:4 warning `§` instead of the word «Section» — `paperlint lint --fix` writes it paper/section-word
154
+ 3:19 warning `.05` has no leading zero — write `0.05` (IEEE / ISO 80000-1); `paperlint lint --fix` inserts it paper/leading-zero
110
155
 
111
- ```json
112
- { "extends": "paperlint:aisec", "kind": "research" }
113
- ```
156
+ ✖ 2 problems (0 errors, 2 warnings)
157
+ 0 errors and 2 warnings potentially fixable with the `--fix` option.
158
+ ```
114
159
 
115
- Settings come in three levels, each named after the tool:
160
+ Errors fail the run (exit 1); warnings only print, unless you pass `--max-warnings <n>`. To keep
161
+ a deliberate exception, put the reason on the line above:
162
+ `% eslint-disable-next-line paper/leading-zero -- quoted from the reviewer`.
116
163
 
117
- ```
118
- package.json "paperlint": { "papersDir": "papers" } the project
119
- papers/my-paper/
120
- paperlint.json { "extends": "paperlint:aisec", … } this paper
121
- paperlint:aisec a venue preset: shipped, or ./your-venue.jsonc the venue
122
- ```
123
-
124
- More in [`docs/configuration.md`](docs/configuration.md#three-levels-of-settings).
125
- `paperlint.json` replaces `venue.json` (2.1.0); `npx paperlint init` moves it.
126
-
127
- A **stage** is a point the paper has reached, such as `submitted` or `camera-ready` (the final
128
- version for the proceedings). The name may use `a-z`, `0-9`, `.`, `_` and `-`. On a folder that
129
- already exists, `paperlint new` adds only the missing files. To use your own templates, put files with
130
- the same names in `papers/.template/`; `{{name}}` in them becomes the paper's name.
164
+ ### 📌 When you submit
131
165
 
132
- Write the paper, then check and build it:
133
-
134
- ```sh
135
- npx paperlint lint # check every paper
136
- npx paperlint build papers/my-paper # writes papers/my-paper/paper.pdf
137
- ```
138
-
139
- `paperlint build` runs pdflatex and bibtex until the references settle. There is no build script to
140
- write; a `build.sh` in the paper folder is ignored.
141
-
142
- When you submit, copy the PDF and `paper.tex` into `versions/` and record the stage in the front
143
- matter of `PIPELINE-STATUS.md`, with the file sizes in bytes:
166
+ Copy the PDF and `paper.tex` into `versions/`, and record the stage in the front matter of
167
+ `PIPELINE-STATUS.md`. You write the sizes yourself (`wc -c < file`):
144
168
 
145
169
  ```yaml
146
170
  stages:
147
171
  - stage: submitted
148
172
  date: 2026-07-22
149
- venue: A Venue 2026
150
173
  pdf: versions/2026-07-22-submitted.pdf
151
174
  bytes: 305412
152
175
  source: versions/2026-07-22-submitted.tex
153
176
  sourceBytes: 57210
154
177
  ```
155
178
 
156
- From then on `paperlint lint` fails if that PDF goes missing or changes size. A frozen PDF should never
157
- change, so a size that no longer matches means the file was replaced after you recorded it
179
+ From then on `paperlint lint` fails if that PDF goes missing or changes size
158
180
  ([`docs/rules.md`](docs/rules.md#the-scorecards-bytes-and-sourcebytes)).
159
181
 
160
- ### What a finding looks like
182
+ ## 🧰 Commands
183
+
184
+ | command | what it does |
185
+ | ----------------------------- | ------------------------------------------------------------------------------ |
186
+ | `npx paperlint init` | sets the project up |
187
+ | `npx paperlint new my-paper` | creates a paper folder from a template; never overwrites a file |
188
+ | `npx paperlint lint` | runs every check over your papers; `--fix` fixes what can be fixed |
189
+ | `npx paperlint build <paper>` | compiles `paper.tex` to `paper.pdf`, measures it, checks the references online |
190
+ | `npx paperlint toolchain` | installs TeX Live with the packages your venues need (~270 MB, ~3 min, once) |
191
+ | `npx paperlint doctor` | checks the setup and exits non-zero if something is miswired |
192
+ | `npx paperlint --help` | every command and flag |
193
+
194
+ ### 🔍 What the checks catch
161
195
 
162
- On a paper folder made by hand, with only `paper.tex`:
196
+ | check | catches |
197
+ | --------------------- | ---------------------------------------------------------------------- |
198
+ | `pdf/fonts` | the template's fonts are missing — LaTeX silently used Computer Modern |
199
+ | `pdf/limits` | more pages than the venue allows for your kind of paper |
200
+ | `paper/stages` | the PDF you recorded as submitted changed or disappeared |
201
+ | `paper/source` | the LaTeX source of a submitted version was not kept |
202
+ | `bib/reachable-entry` | a reference with no DOI, URL or arXiv id |
203
+ | `paper/author-list` | a reference lists the preprint's authors, not the published version's |
204
+
205
+ Every check: [`docs/rules.md`](docs/rules.md). Checks only some venues need are off until you
206
+ turn them on: [`docs/optional-rules.md`](docs/optional-rules.md).
207
+
208
+ ## 🧩 Configuration
209
+
210
+ Two levels, one file name, both optional:
163
211
 
164
212
  ```
165
- config: package.json
166
- papers/my-paper
167
- error missing `PIPELINE-STATUS.md` — `paper/stages`, `paper/source` and `paper/author-list` read this file, so nothing `my-paper` declares about its stages, sources or authors is checked
168
- …/my-papers/papers/my-paper/paper.tex
169
- 1:1 warning 1 × `§` instead of «Section» (reviewer B). Paying the debt down is silent; growth is reported paper/typography
170
- 1:1 warning 1 × a decimal without a leading zero, `.05` instead of `0.05` (IEEE / ISO 80000-1 style). Paying the debt down is silent; growth is reported paper/typography
171
-
172
- ✖ 2 problems (0 errors, 2 warnings)
213
+ paperlint.json the project: papersDir, rules, defaults for every paper
214
+ papers/my-paper/paperlint.json one paper: its venue, its kind, its own rules
215
+ ```
216
+
217
+ `papersDir` defaults to `papers`. If your papers live elsewhere, say so in a `paperlint.json`
218
+ beside your `package.json`:
219
+
220
+ ```json
221
+ { "papersDir": "docs/papers" }
173
222
  ```
174
223
 
175
- Each finding gives the file, line and column, the level, what is wrong, and the check that found
176
- it. The first line is an error about the folder: without `PIPELINE-STATUS.md`, the three checks
177
- that read it have nothing to check; `npx paperlint new my-paper` adds the file and leaves `paper.tex`
178
- alone. The two warnings count typography slips against an allowance you can set per paper: going over
179
- it is reported, fixing some is not ("paying the debt down is silent").
224
+ - A paper's file merges over the root's: its `extends` and `kind` win, its `rules` apply last.
225
+ - Only the paper files under `papersDir` are linted, never a `repro/` script beside a paper.
226
+ - An unknown key is an error, so a typo cannot silently turn a setting off.
180
227
 
181
- `paperlint lint` exits `1` when any check reports an error (as here, even though the summary line counts
182
- only the warnings in files), or when it checked no files at all (usually a wrong path). Warnings
183
- never fail the run unless you pass `--max-warnings <n>`. `--json` prints the findings as JSON.
228
+ Every key: [`docs/configuration.md`](docs/configuration.md).
184
229
 
185
- ## Run it in CI
230
+ ### Another venue
186
231
 
187
- `paperlint init` offers to write this GitHub Actions workflow step for you. By hand:
232
+ Write a preset in your repository, and extend it by path from the paper's `paperlint.json`
233
+ (`"extends": "../../venues/my-venue.jsonc"`):
234
+
235
+ ```jsonc
236
+ {
237
+ "extends": "paperlint:acm-sigconf",
238
+ "format": { "kinds": { "short": { "body_pages_max": 4 } } },
239
+ }
240
+ ```
241
+
242
+ The full shape: [`docs/rules.md`](docs/rules.md#writing-your-own-venue-preset).
243
+
244
+ ## 🤖 Run it in CI
245
+
246
+ `paperlint init` offers to write this workflow for you, pinned to the version you installed. By
247
+ hand:
188
248
 
189
249
  ```yaml
190
- - uses: zernie/paperlint@v2.0.0
250
+ - uses: zernie/paperlint@v3.0.0
191
251
  with:
192
252
  paths: papers
193
253
  ```
194
254
 
195
- Use the tag of the version you installed (`npm ls paperlint`): every npm release has
196
- a git tag of the same version, so the step runs the same code as your package. It runs
197
- `paperlint lint`. `paths` is required, and the step fails if it checked zero files, so a typo in the path
198
- shows up red. Optional inputs: `config`, `max-warnings` (default `-1`, no limit), `texcount` (default
199
- `true`), `working-directory`.
200
-
201
- ## What the checks catch
202
-
203
- | check | level | catches |
204
- | ------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------- |
205
- | `paper/stages` | error | a stage's PDF is missing, or it is not the same file any more (its size changed) |
206
- | `paper/source` | error | a stage has no frozen source file next to its PDF |
207
- | `paper/author-list` | warning | a stage is declared, but `PIPELINE-STATUS.md` does not record that the author list was checked |
208
- | `paper/research-question` | warning | the research question is missing from `PIPELINE-STATUS.md`, or the paper does not contain that sentence |
209
- | `paper/typography` | warning | more `§`, `.05`-style decimals, mixed `Fig.`/`Figure`, or references without a DOI or URL than you allowed |
210
- | `tex/future-promise` | warning | a camera-ready still says your code "will be released" |
211
- | `tex/acm-frontmatter-override` | error | an ACM paper overrides the template's title-page commands, so parts of page 1 go missing |
212
- | `review/findings-cause` | error | a review note lists several findings and names no cause for any of them |
213
- | `doc/fields` | warning | a review note's front matter is missing a field you require (off unless configured) |
214
- | `pdf/limits` | error | more body or reference pages than the venue allows for your kind of paper, or a reference font size out of range |
215
- | `pdf/fonts` | error | a Type 3 or unembedded font, or the venue template's fonts are missing (a silent Computer Modern fallback) |
216
- | `pdf/geometry` | error | the paper size or column count differs from the venue's |
217
- | `pdf/body-size` | warning | the body font size is off the venue's |
218
- | `pdf/profile` | error | the paper's `extends` names no preset (a typo), or a kind of paper the venue does not have |
219
- | `pdf/fresh` | error | the build facts describe an earlier PDF than the one on disk |
220
- | `pdf/measured` | warning | the venue checks did not run: the paper names no venue preset yet, or was not built |
221
-
222
- The `pdf/` checks run only for a paper whose `paperlint.json` extends a venue preset, and they judge the PDF
223
- `paperlint build` made: build, then lint. Errors fail the run; warnings only print. What each check
224
- reads: [`docs/rules.md`](docs/rules.md).
225
- Checks that only some venues need are off until you turn them on:
226
- [`docs/optional-rules.md`](docs/optional-rules.md).
227
-
228
- ## Configuration
229
-
230
- `paperlint init` writes the one required setting into `package.json`:
255
+ - Pin the tag of the version you installed: `npm ls paperlint` prints it.
256
+ - The step runs `paperlint lint`. It does not build, so CI needs no TeX Live.
231
257
 
232
- ```json
233
- {
234
- "paperlint": {
235
- "papersDir": "papers"
236
- }
237
- }
238
- ```
258
+ ## ❓ FAQ
239
259
 
240
- `papersDir` has no default, so the tool never checks a folder you did not choose: without it,
241
- `paperlint lint` stops and says so, unless you pass a path. An unknown key is an error. The optional
242
- settings, and how to use the checks inside your own ESLint setup, are in
243
- [`docs/configuration.md`](docs/configuration.md). TeX Live, its install location and the external
244
- programs the skills use are in [`docs/toolchain.md`](docs/toolchain.md).
260
+ **Does it install anything without asking?**
261
+ No. TeX Live comes only from `paperlint toolchain`, or when you say yes in `init` or `build`. On
262
+ Windows, install TeX Live yourself.
245
263
 
246
- ## Claude Code (optional)
264
+ **Will `new` or `init` overwrite my files?**
265
+ No. They only add what is missing, and `init` keeps a `papersDir` you already declared.
247
266
 
248
- `paperlint lint` needs only Node. If you use Claude Code, `paperlint init` also sets up two things:
267
+ **My venue isn't listed — what now?**
268
+ Write [a four-line preset](#another-venue) that extends the closest template family. For any ACM
269
+ venue, `paperlint:acm-sigconf` already checks the format, without a page limit.
249
270
 
250
- **Skills** — one per stage of writing a paper, from checking the idea through drafting, review,
251
- submission and camera-ready. They are linked into `.claude/skills/`. Start with `/paper-pipeline`;
252
- it routes to the rest. Some call Java or Python 3, which you install yourself.
271
+ **Do I need TeX Live just to lint?**
272
+ No. `paperlint lint` needs only Node. `paperlint build` needs TeX Live.
253
273
 
254
- **Hooks** — written into `.claude/settings.json`. Commit that file so every clone gets them; a
255
- fresh clone needs `npm install` before they run.
274
+ **What does a CI failure look like?**
275
+ The step fails, and its log lists each finding with file, line, check and message. It also fails
276
+ when it checked zero files, so a wrong `paths` shows up red.
256
277
 
257
- | hook | blocks? | what it does |
258
- | -------------------- | ------- | ---------------------------------------------------------------- |
259
- | `paper-edit-guard` | yes | stops a shell command from writing to a paper file |
260
- | `paper-skills-nudge` | no | after a paper edit, shows the agent the pre-submission checklist |
261
- | `paper-status-gates` | no | after a paper edit, lists the stages that have not run yet |
278
+ **Several papers for different venues in one repo?**
279
+ Yes. Each paper names its own venue in its own `paperlint.json`.
262
280
 
263
- **Know this about `paper-edit-guard`:** if the `paperlint` key in `package.json`
264
- cannot be read (the file is missing, or has merge-conflict markers), it blocks **every** shell
265
- command in Claude Code, not only paper ones, until the key is readable again. Fix `package.json`
266
- with a normal file edit — those are not blocked.
281
+ **Where does my configuration live?**
282
+ In `paperlint.json` — at the project root (optional) and in each paper. Nothing goes into
283
+ `package.json`.
267
284
 
268
- ## What it does not do
285
+ **Do I need Claude Code?**
286
+ No. The linter needs only Node. With Claude Code, `init` also links the skills and wires three
287
+ hooks into `.claude/settings.json`: one stops a shell command from writing to a paper file, two
288
+ remind the agent what is left after a paper edit ([`docs/install.md`](docs/install.md)).
269
289
 
270
- - **It does not install software behind your back.** `paperlint init` installs nothing; TeX Live comes
271
- only from `paperlint toolchain` or when you answer yes in `paperlint build`. On Windows, install TeX Live
272
- yourself.
273
- - **It does not guess what to check.** There is no default papers directory.
274
- - **It does not overwrite your files.** `paperlint new` and `paperlint init` only add what is missing.
275
- - **It does not run your build script.** `paperlint build` compiles the paper itself.
276
- - **It does not write or grade the paper.** `paperlint lint` checks records and a few mechanical
277
- mistakes; judging the writing is what the optional skills are for.
278
- - **New papers are LaTeX.** Markdown papers (`paper.md`) are still read but deprecated
279
- ([#57](https://github.com/zernie/paperlint/issues/57)).
280
- - **No Yarn Plug'n'Play.** npm and pnpm are supported
281
- ([`docs/install.md`](docs/install.md#package-managers)).
290
+ **Does it change my paper?**
291
+ Only `paperlint lint --fix`, and only three rules: `paper/section-word` (`§` → Section),
292
+ `paper/leading-zero` (`.05` → `0.05`) and `paper/figure-ref-style` (one figure-reference style).
282
293
 
283
- ## Contributing
294
+ ## 📚 Docs
284
295
 
285
- See [`CONTRIBUTING.md`](CONTRIBUTING.md): how the package is tested, the one command that runs
286
- every check, and how releases work: the pull-request title decides the version (`feat:` is a
287
- minor release, `fix:` a patch).
296
+ - [`docs/install.md`](docs/install.md) — what `init` does, package managers, troubleshooting
297
+ - [`docs/configuration.md`](docs/configuration.md) — every setting, and how `build` compiles
298
+ - [`docs/rules.md`](docs/rules.md) — every check, what it reads and when it fails
299
+ - [`docs/optional-rules.md`](docs/optional-rules.md) — checks only some venues need
300
+ - [`docs/toolchain.md`](docs/toolchain.md) — TeX Live, and Banal (HotCRP's page-geometry checker, GPL)
301
+ - [`CONTRIBUTING.md`](CONTRIBUTING.md) — how the package is tested and released
288
302
 
289
303
  ## License
290
304
 
291
- MIT.
292
-
293
- **banal is not part of this package.** banal, the page-geometry script from
294
- [HotCRP](https://github.com/kohler/hotcrp) (Geoffrey M. Voelker, Eddie Kohler), is licensed
295
- GPL-2.0-or-later. paperlint does not contain, copy or modify it: `paperlint toolchain` downloads it from HotCRP
296
- at a pinned commit, checks its sha256, and paperlint runs it as a separate program (`perl banal …`),
297
- reading its JSON output. Details: [`docs/toolchain.md`](docs/toolchain.md#page-geometry-banal-without-poppler).
305
+ MIT. Banal, used for page geometry, is GPL and not part of this package:
306
+ [`docs/toolchain.md`](docs/toolchain.md#page-geometry-banal-without-poppler).
package/action.yml CHANGED
@@ -26,11 +26,9 @@ description: Install the toolchain the texcount/* rules need, run ESLint, and re
26
26
  inputs:
27
27
  config:
28
28
  description: >-
29
- Path to the `package.json` holding the `paperlint` settings. Optional: `paperlint lint` finds it by walking up from the working directory,
30
- the way eslint and tsc find theirs, so most callers never set this. Pass it only when the
31
- file is somewhere the walk cannot reach. ⚠️ Until 2026-09-18 this input took a path to an
32
- ESLint FLAT CONFIG and defaulted to `eslint.config.mjs`; a caller still passing one will
33
- fail loudly on the first run rather than lint against it.
29
+ Path to the project's root `paperlint.json`. Optional, and so is the file: `paperlint lint`
30
+ finds it by walking up from the working directory, the way eslint and tsc find theirs, so
31
+ most callers never set this. Pass it only when the file is somewhere the walk cannot reach.
34
32
  required: false
35
33
  default: ""
36
34
  paths:
@@ -135,4 +133,4 @@ runs:
135
133
  npx paperlint lint $PAPERLINT_PATHS ${PAPERLINT_CONFIG:+--config "$PAPERLINT_CONFIG"} \
136
134
  --max-warnings="$PAPERLINT_MAXWARN" --json > "$RUNNER_TEMP/paperlint-report.json" || RC=$?
137
135
  node "$GITHUB_ACTION_PATH/scripts/eslint-report-guard.mjs" \
138
- "$RUNNER_TEMP/paperlint-report.json" "$RC" "$PAPERLINT_PATHS" "${PAPERLINT_CONFIG:-package.json}"
136
+ "$RUNNER_TEMP/paperlint-report.json" "$RC" "$PAPERLINT_PATHS" "${PAPERLINT_CONFIG:-paperlint.json}"
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The online reference checks, as the `CheckReferences` port: `verify-cites` (the cited work
3
+ * exists, the title matches — Crossref, OpenAlex, Semantic Scholar, arXiv) and `bib-authors` (the
4
+ * authors are the published version's — DBLP). Both ship in this package's verify-citations skill;
5
+ * this adapter only runs them and shapes their answers.
6
+ *
7
+ * "Not checked" is decided up front, by one request: when Crossref cannot be reached at all, the
8
+ * checkers would degrade every entry to `unresolvable` / `unchecked`, which reads like a result.
9
+ * Saying `not-checked` with the reason is the honest record.
10
+ */
11
+ import type { CheckReferences } from "../../ports/check-references.ts";
12
+ export declare const onlineReferences: CheckReferences;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/references/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EACV,eAAe,EAEhB,MAAM,iCAAiC,CAAC;AAwEzC,eAAO,MAAM,gBAAgB,EAAE,eAgB9B,CAAC"}