paperlint 2.0.1 → 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 (226) hide show
  1. package/CLAUDE.md +6 -1
  2. package/CONTRIBUTING.md +21 -0
  3. package/README.md +231 -180
  4. package/action.yml +4 -6
  5. package/dist/adapters/references/index.d.ts +13 -0
  6. package/dist/adapters/references/index.d.ts.map +1 -0
  7. package/dist/adapters/references/index.js +55 -0
  8. package/dist/adapters/references/index.js.map +1 -0
  9. package/dist/adapters/references/reach.io.d.ts +3 -0
  10. package/dist/adapters/references/reach.io.d.ts.map +1 -0
  11. package/dist/adapters/references/reach.io.js +14 -0
  12. package/dist/adapters/references/reach.io.js.map +1 -0
  13. package/dist/build-engine.d.ts.map +1 -1
  14. package/dist/build-engine.js +5 -2
  15. package/dist/build-engine.js.map +1 -1
  16. package/dist/build.d.ts +16 -1
  17. package/dist/build.d.ts.map +1 -1
  18. package/dist/build.js +40 -5
  19. package/dist/build.js.map +1 -1
  20. package/dist/cli.d.ts +37 -20
  21. package/dist/cli.d.ts.map +1 -1
  22. package/dist/cli.js +377 -251
  23. package/dist/cli.js.map +1 -1
  24. package/dist/doctor.d.ts.map +1 -1
  25. package/dist/doctor.js +37 -52
  26. package/dist/doctor.js.map +1 -1
  27. package/dist/facts-file.d.ts +33 -9
  28. package/dist/facts-file.d.ts.map +1 -1
  29. package/dist/facts-file.js +117 -17
  30. package/dist/facts-file.js.map +1 -1
  31. package/dist/hooks-settings.d.ts +0 -21
  32. package/dist/hooks-settings.d.ts.map +1 -1
  33. package/dist/hooks-settings.js +17 -135
  34. package/dist/hooks-settings.js.map +1 -1
  35. package/dist/init.d.ts +40 -19
  36. package/dist/init.d.ts.map +1 -1
  37. package/dist/init.js +144 -109
  38. package/dist/init.js.map +1 -1
  39. package/dist/link-skills.d.ts +1 -1
  40. package/dist/link-skills.d.ts.map +1 -1
  41. package/dist/link-skills.js +11 -29
  42. package/dist/link-skills.js.map +1 -1
  43. package/dist/new-paper.d.ts +2 -2
  44. package/dist/new-paper.d.ts.map +1 -1
  45. package/dist/new-paper.js +6 -2
  46. package/dist/new-paper.js.map +1 -1
  47. package/dist/paper-files.d.ts +63 -0
  48. package/dist/paper-files.d.ts.map +1 -0
  49. package/dist/paper-files.js +69 -0
  50. package/dist/paper-files.js.map +1 -0
  51. package/dist/paper-settings.d.ts +40 -0
  52. package/dist/paper-settings.d.ts.map +1 -0
  53. package/dist/paper-settings.js +144 -0
  54. package/dist/paper-settings.js.map +1 -0
  55. package/dist/ports/check-references.d.ts +31 -0
  56. package/dist/ports/check-references.d.ts.map +1 -0
  57. package/dist/ports/check-references.js +7 -0
  58. package/dist/ports/check-references.js.map +1 -0
  59. package/dist/presets.d.ts +84 -0
  60. package/dist/presets.d.ts.map +1 -0
  61. package/dist/presets.js +184 -0
  62. package/dist/presets.js.map +1 -0
  63. package/dist/reference-rules.d.ts +47 -0
  64. package/dist/reference-rules.d.ts.map +1 -0
  65. package/dist/reference-rules.js +156 -0
  66. package/dist/reference-rules.js.map +1 -0
  67. package/dist/references.d.ts +36 -0
  68. package/dist/references.d.ts.map +1 -0
  69. package/dist/references.js +91 -0
  70. package/dist/references.js.map +1 -0
  71. package/dist/rules-config.d.ts +11 -2
  72. package/dist/rules-config.d.ts.map +1 -1
  73. package/dist/rules-config.js +25 -11
  74. package/dist/rules-config.js.map +1 -1
  75. package/dist/structure.d.ts.map +1 -1
  76. package/dist/structure.js +3 -2
  77. package/dist/structure.js.map +1 -1
  78. package/dist/tex-requirements.d.ts +63 -7
  79. package/dist/tex-requirements.d.ts.map +1 -1
  80. package/dist/tex-requirements.js +91 -24
  81. package/dist/tex-requirements.js.map +1 -1
  82. package/dist/types.d.ts +20 -18
  83. package/dist/types.d.ts.map +1 -1
  84. package/dist/venue-rules.d.ts +109 -0
  85. package/dist/venue-rules.d.ts.map +1 -0
  86. package/dist/venue-rules.js +389 -0
  87. package/dist/venue-rules.js.map +1 -0
  88. package/docs/configuration.md +152 -77
  89. package/docs/e2e.md +1 -1
  90. package/docs/install.md +25 -40
  91. package/docs/optional-rules.md +26 -24
  92. package/docs/rules.md +181 -15
  93. package/eslint-rules/bib-reachable-entry.mjs +75 -0
  94. package/eslint-rules/latex-language.mjs +29 -1
  95. package/eslint-rules/paper-stages.harness.mjs +1 -117
  96. package/eslint-rules/paper-stages.mjs +0 -120
  97. package/eslint-rules/paper-stages.mutations.mjs +0 -76
  98. package/eslint-rules/paper-typography.mjs +267 -192
  99. package/eslint-rules/paper-typography.test.mjs +297 -0
  100. package/eslint-rules/papers.harness.mjs +8 -12
  101. package/eslint-rules/papers.mjs +17 -27
  102. package/eslint-rules/pdf-last-page-balance.mjs +23 -5
  103. package/eslint-rules/review-frontmatter.mjs +118 -0
  104. package/eslint-rules/review-frontmatter.schema.json +36 -0
  105. package/eslint-rules/review-frontmatter.test.mjs +118 -0
  106. package/eslint-rules/sibling-frontmatter.mjs +19 -0
  107. package/eslint-rules/sibling-frontmatter.schema.json +14 -0
  108. package/eslint.config.mjs +0 -15
  109. package/fixtures/build-e2e/acmart/paperlint.json +1 -0
  110. package/fixtures/real-markdown-paper/baseline.json +6 -7
  111. package/hooks/hooks.harness.mjs +39 -98
  112. package/hooks/hooks.mutations.mjs +6 -6
  113. package/hooks/paper-edit-guard.hook.d.mts +2 -3
  114. package/hooks/paper-edit-guard.hook.mjs +31 -42
  115. package/hooks/paper-skills-nudge.hook.mjs +21 -19
  116. package/hooks/paper-status-gates.hook.mjs +21 -19
  117. package/hooks/paper-status-gates.sh +8 -13
  118. package/lib/paper-config.d.mts +15 -18
  119. package/lib/paper-config.harness.mjs +18 -35
  120. package/lib/paper-config.mjs +90 -81
  121. package/lib/paper-config.mutations.mjs +2 -2
  122. package/lib/skill-checks.mjs +1 -1
  123. package/lib/skill-trigger-cases.harness.mjs +9 -8
  124. package/lib/skill-trigger-cases.mjs +7 -15
  125. package/package.json +1 -1
  126. package/scripts/eslint-report-guard.mjs +1 -1
  127. package/scripts/harness-api.frozen.json +0 -3
  128. package/scripts/mutation-batteries.frozen.json +4 -13
  129. package/skills/analyze-sibling-paper/SKILL.md +16 -12
  130. package/skills/analyze-sibling-paper/SKILL.md.spec.ts +15 -11
  131. package/skills/cold-read-diff/SKILL.md +20 -1
  132. package/skills/cold-read-diff/SKILL.md.spec.ts +19 -0
  133. package/skills/find-venue/SKILL.md +5 -1
  134. package/skills/find-venue/SKILL.md.spec.ts +4 -0
  135. package/skills/grade-paper-writing/SKILL.md +20 -1
  136. package/skills/grade-paper-writing/SKILL.md.spec.ts +19 -0
  137. package/skills/paper-pipeline/PIPELINE-MAP.md +14 -4
  138. package/skills/paper-pipeline/SKILL.md +4 -3
  139. package/skills/paper-pipeline/SKILL.md.spec.ts +3 -2
  140. package/skills/paper-pipeline/scripts/consumer.d.mts +0 -1
  141. package/skills/paper-pipeline/scripts/consumer.harness.mjs +8 -12
  142. package/skills/paper-pipeline/scripts/consumer.mjs +19 -33
  143. package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +6 -4
  144. package/skills/paper-pipeline/scripts/run-mechanical.mjs +2 -11
  145. package/skills/plan-paper-timeline/SKILL.md +2 -2
  146. package/skills/plan-paper-timeline/SKILL.md.spec.ts +1 -1
  147. package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +1 -1
  148. package/skills/plan-paper-timeline/plan-paper-timeline.effects.mutations.mjs +2 -2
  149. package/skills/render-paper/check-render.sh +2 -3
  150. package/skills/render-paper/extract-pdf-facts.harness.mjs +5 -5
  151. package/skills/render-paper/extract-pdf-facts.mjs +4 -4
  152. package/skills/render-paper/render-paper.harness.mjs +2 -2
  153. package/skills/study-accepted-papers/SKILL.md +2 -1
  154. package/skills/study-accepted-papers/SKILL.md.spec.ts +1 -0
  155. package/skills/submit-paper/SKILL.md +9 -1
  156. package/skills/submit-paper/SKILL.md.spec.ts +8 -0
  157. package/skills/submit-paper/references/venues/acm-sigconf.jsonc +86 -0
  158. package/skills/submit-paper/references/venues/agenticdev.jsonc +34 -102
  159. package/skills/submit-paper/references/venues/aisec.jsonc +21 -82
  160. package/skills/submit-paper/references/venues/realm.jsonc +45 -43
  161. package/skills/submit-paper/references/venues/tex-base.jsonc +1 -1
  162. package/skills/submit-paper/references/venues/venue-profile.schema.json +119 -29
  163. package/skills/verify-citations/SKILL.md +10 -1
  164. package/skills/verify-citations/SKILL.md.spec.ts +9 -0
  165. package/skills/verify-citations/scripts/bib-authors.mjs +48 -26
  166. package/src/adapters/references/index.ts +102 -0
  167. package/src/adapters/references/reach.io.ts +12 -0
  168. package/src/build-engine.ts +10 -2
  169. package/src/build.harness.mjs +31 -22
  170. package/src/build.ts +65 -8
  171. package/src/cli.harness.mjs +151 -294
  172. package/src/cli.mutations.mjs +35 -44
  173. package/src/cli.ts +457 -288
  174. package/src/doctor.harness.mjs +41 -84
  175. package/src/doctor.mutations.mjs +9 -9
  176. package/src/doctor.ts +41 -61
  177. package/src/facts-file.test.ts +47 -7
  178. package/src/facts-file.ts +145 -24
  179. package/src/hooks-settings.harness.mjs +7 -92
  180. package/src/hooks-settings.mutations.mjs +2 -17
  181. package/src/hooks-settings.ts +18 -162
  182. package/src/init.test.ts +206 -0
  183. package/src/init.ts +193 -159
  184. package/src/link-skills.harness.mjs +1 -38
  185. package/src/link-skills.mutations.mjs +9 -5
  186. package/src/link-skills.ts +10 -34
  187. package/src/lint-fix.test.ts +120 -0
  188. package/src/new-paper.harness.mjs +9 -6
  189. package/src/new-paper.test.ts +133 -0
  190. package/src/new-paper.ts +7 -2
  191. package/src/paper-files.test.ts +290 -0
  192. package/src/paper-files.ts +144 -0
  193. package/src/paper-settings-commands.test.ts +274 -0
  194. package/src/paper-settings.test.ts +249 -0
  195. package/src/paper-settings.ts +208 -0
  196. package/src/ports/check-references.ts +28 -0
  197. package/src/presets.test.ts +236 -0
  198. package/src/presets.ts +297 -0
  199. package/src/reference-rules.ts +229 -0
  200. package/src/references.test.ts +209 -0
  201. package/src/references.ts +138 -0
  202. package/src/rules-config.harness.mjs +24 -8
  203. package/src/rules-config.ts +30 -13
  204. package/src/structure.harness.mjs +6 -4
  205. package/src/structure.ts +3 -2
  206. package/src/tex-requirements.harness.mjs +11 -17
  207. package/src/tex-requirements.ts +179 -29
  208. package/src/types.ts +30 -18
  209. package/src/venue-rules.test.ts +554 -0
  210. package/src/venue-rules.ts +571 -0
  211. package/templates/paper/paperlint.json +4 -0
  212. package/eslint-rules/doc-fields.harness.mjs +0 -336
  213. package/eslint-rules/doc-fields.mjs +0 -186
  214. package/eslint-rules/doc-fields.mutations.mjs +0 -96
  215. package/eslint-rules/paper-typography.harness.mjs +0 -291
  216. package/eslint-rules/paper-typography.mutations.mjs +0 -131
  217. package/eslint-rules/review-findings-cause.harness.mjs +0 -228
  218. package/eslint-rules/review-findings-cause.mjs +0 -135
  219. package/eslint-rules/review-findings-cause.mutations.mjs +0 -72
  220. package/fixtures/build-e2e/acmart/venue.json +0 -1
  221. package/fixtures/paper-stages/authors-ran/PIPELINE-STATUS.md +0 -16
  222. package/fixtures/paper-stages/marker-in-prose/PIPELINE-STATUS.md +0 -17
  223. package/fixtures/review-findings-cause/clean.md +0 -17
  224. package/fixtures/review-findings-cause/defect.md +0 -14
  225. package/fixtures/review-findings-cause/old-debt.md +0 -14
  226. 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/CONTRIBUTING.md CHANGED
@@ -109,6 +109,27 @@ node scripts/run-mutations.mjs # run the remaining batteries (deprecated, #52
109
109
  None of these are needed to USE the tool — they are here because the gates are part of the
110
110
  argument, not decoration.
111
111
 
112
+ ## Adding a venue
113
+
114
+ A venue is a **preset**, a JSONC file in `skills/submit-paper/references/venues/`, validated by
115
+ `venue-profile.schema.json` beside it. Adding one is three files and no code:
116
+
117
+ 1. **The preset, thin.** `venues/<name>.jsonc` extends the template family it is built on
118
+ (`"extends": "paperlint:acm-sigconf"` for an ACM venue) and adds only what the call for papers
119
+ sets: `format.kinds` (the page limit of each kind of paper) and, when the venue's producer asks
120
+ for something an optional rule checks, `rules`. Every number carries the quote it came from.
121
+ A venue on a template with no family yet stands alone (`template`, `tex`, `format`) — or, better,
122
+ add the family first: measured on a real template build (banal + pdf.js), not copied from
123
+ documentation ([#88](https://github.com/zernie/paperlint/issues/88)).
124
+ 2. **The card.** `venues/<name>.md` — prose about the venue: deadlines, tracks, the blind model,
125
+ what the form asks.
126
+ 3. **A test.** A case in `src/presets.test.ts` that `paperlint:<name>` resolves over its family with
127
+ the kinds you declared; `src/tex-requirements.harness.mjs` already checks every shipped preset
128
+ against the schema.
129
+
130
+ `paperlint toolchain` picks the new preset's packages up by itself, and the README and
131
+ [`docs/rules.md`](docs/rules.md#checks-against-the-venue) list the shipped presets — update both.
132
+
112
133
  ## Releases
113
134
 
114
135
  Every push to `main` runs semantic-release (`.github/workflows/release.yml`). The squash commit —
package/README.md CHANGED
@@ -1,255 +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
- - **Builds the same PDF everywhere.** `paperlint toolchain` installs TeX Live with exactly the
16
- packages your venue's template needs and checks that each one is really there, so the silent
17
- font switch cannot happen and your laptop and CI build with the same TeX Live.
18
- - **Keeps what you submitted.** Record "submitted on 22 July, as this PDF", and paperlint fails if
19
- that PDF changes or disappears, or its LaTeX source was not kept beside it.
20
- - **Knows the classic slips.** An ACM paper that overrides the title-page commands and loses part
21
- of page 1; "code will be released" left in a camera-ready; references without a DOI or URL; an
22
- unbalanced last page, for publishers that ask for balanced columns (an optional check).
23
- - **Works with coding agents.** Hooks and skills for Claude Code run the checks after every edit
24
- and stop an agent from rewriting the paper around them.
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
25
92
 
26
- It is for researchers and engineers who write papers in LaTeX inside git and submit them to
27
- conferences or journals.
93
+ <!-- `vigiles:symbol src/init.ts#init` — `npm run check` fails if this function is renamed or removed. -->
28
94
 
29
- ## Install and set up
95
+ 1. Install (Node 22.13 or newer):
30
96
 
31
- <!-- `vigiles:symbol src/init.ts#init` — `npm run check` fails if this function is renamed or removed. -->
97
+ ```sh
98
+ npm i -D paperlint
99
+ ```
32
100
 
33
- ```sh
34
- npm i -D paperlint
35
- npx paperlint init
36
- ```
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:
37
103
 
38
- Node 22.13 or newer. Install first: `paperlint init` links the skills and hooks to the copy in
39
- your project's `node_modules`, so run without the install it has nothing to link to.
104
+ ```sh
105
+ npx paperlint init
106
+ ```
40
107
 
41
- `paperlint init` finds your papers directory and records it in `package.json`, offers a CI workflow and a
42
- first paper, and sets up the optional Claude Code skills and hooks. It installs no software. It
43
- asks questions only when you run it in a terminal; an agent, CI or `--yes` gets the defaults, and
44
- each default it takes is printed. It ends by running `paperlint doctor`, which checks the setup.
45
- Exactly what it writes: [`docs/install.md`](docs/install.md#what-paperlint-init-writes).
108
+ 3. TeX Live, for building — once. `init` offers it; `paperlint lint` alone needs no TeX:
46
109
 
47
- ## What you get
110
+ ```sh
111
+ npx paperlint toolchain # ~270 MB, ~3 min, once
112
+ ```
48
113
 
49
- | command | what it does |
50
- | ----------------------------- | -------------------------------------------------------------------------------------------------- |
51
- | `npx paperlint init` | sets the project up (see above) |
52
- | `npx paperlint new my-paper` | creates a paper folder from a template; never overwrites a file |
53
- | `npx paperlint lint` | runs every check over your papers; `npx paperlint lint papers/my-paper` checks one |
54
- | `npx paperlint build <paper>` | compiles `paper.tex` to `paper.pdf` with pdflatex and bibtex; `--all` builds every paper |
55
- | `npx paperlint toolchain` | installs TeX Live with the LaTeX packages your venues need; `--check` only reports what is missing |
56
- | `npx paperlint doctor` | checks the setup and exits non-zero if something is miswired |
57
- | `npx paperlint --help` | every command and flag |
114
+ 4. Create a paper:
58
115
 
59
- A venue is the conference or journal you submit to. TeX Live is the standard LaTeX distribution;
60
- `paperlint build` offers to install it the first time it needs it.
116
+ <!-- `vigiles:symbol src/new-paper.ts#newPaper` — `npm run check` fails if this function is renamed or removed. -->
61
117
 
62
- ## Your first paper in five minutes
118
+ ```sh
119
+ npx paperlint new my-paper
120
+ ```
63
121
 
64
- ### Starting a paper
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
+ ```
65
128
 
66
- <!-- `vigiles:symbol src/new-paper.ts#newPaper` — `npm run check` fails if this function is renamed or removed. -->
129
+ 5. Pick the venue ([the table above](#-supported-venues)) in `papers/my-paper/paperlint.json`:
67
130
 
68
- After `npx paperlint init`:
131
+ ```json
132
+ { "extends": "paperlint:agenticdev", "kind": "short" }
133
+ ```
69
134
 
70
- ```sh
71
- npx paperlint new my-paper
72
- ```
135
+ 6. Build the PDF:
73
136
 
74
- ```
75
- ✓ created papers/my-paper
76
- + PIPELINE-STATUS.md (from the package template)
77
- + paper.tex (from the package template)
137
+ ```sh
138
+ npx paperlint build papers/my-paper
139
+ ```
78
140
 
79
- config: package.json
80
- ✓ 2 file(s) checked, no findings
81
- ```
141
+ 7. Check every paper:
82
142
 
83
- You now have one folder per paper:
143
+ ```sh
144
+ npx paperlint lint
145
+ ```
84
146
 
85
- ```
86
- papers/
87
- my-paper/
88
- paper.tex the paper, in LaTeX
89
- PIPELINE-STATUS.md the paper's record: its research question and the stages it reached
90
- reviews/*.md review notes (optional)
91
- versions/ the exact PDF and source you sent at each stage, never edited
92
- ```
147
+ Sample output, on a paper with two slips and no `PIPELINE-STATUS.md`:
93
148
 
94
- A **stage** is a point the paper has reached, such as `submitted` or `camera-ready` (the final
95
- version for the proceedings). The name may use `a-z`, `0-9`, `.`, `_` and `-`. On a folder that
96
- already exists, `paperlint new` adds only the missing files. To use your own templates, put files with
97
- the same names in `papers/.template/`; `{{name}}` in them becomes the paper's name.
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
98
155
 
99
- Write the paper, then check and build it:
156
+ ✖ 2 problems (0 errors, 2 warnings)
157
+ 0 errors and 2 warnings potentially fixable with the `--fix` option.
158
+ ```
100
159
 
101
- ```sh
102
- npx paperlint lint # check every paper
103
- npx paperlint build papers/my-paper # writes papers/my-paper/paper.pdf
104
- ```
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`.
105
163
 
106
- `paperlint build` runs pdflatex and bibtex until the references settle. There is no build script to
107
- write; a `build.sh` in the paper folder is ignored.
164
+ ### 📌 When you submit
108
165
 
109
- When you submit, copy the PDF and `paper.tex` into `versions/` and record the stage in the front
110
- 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`):
111
168
 
112
169
  ```yaml
113
170
  stages:
114
171
  - stage: submitted
115
172
  date: 2026-07-22
116
- venue: A Venue 2026
117
173
  pdf: versions/2026-07-22-submitted.pdf
118
174
  bytes: 305412
119
175
  source: versions/2026-07-22-submitted.tex
120
176
  sourceBytes: 57210
121
177
  ```
122
178
 
123
- From then on `paperlint lint` fails if that PDF goes missing or changes size. A frozen PDF should never
124
- 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
125
180
  ([`docs/rules.md`](docs/rules.md#the-scorecards-bytes-and-sourcebytes)).
126
181
 
127
- ### What a finding looks like
182
+ ## 🧰 Commands
128
183
 
129
- On a paper folder made by hand, with only `paper.tex`:
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 |
130
193
 
131
- ```
132
- config: package.json
133
- papers/my-paper
134
- 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
135
- …/my-papers/papers/my-paper/paper.tex
136
- 1:1 warning 1 × `§` instead of «Section» (reviewer B). Paying the debt down is silent; growth is reported paper/typography
137
- 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
138
-
139
- ✖ 2 problems (0 errors, 2 warnings)
140
- ```
194
+ ### 🔍 What the checks catch
141
195
 
142
- Each finding gives the file, line and column, the level, what is wrong, and the check that found
143
- it. The first line is an error about the folder: without `PIPELINE-STATUS.md`, the three checks
144
- that read it have nothing to check; `npx paperlint new my-paper` adds the file and leaves `paper.tex`
145
- alone. The two warnings count typography slips against an allowance you can set per paper: going over
146
- it is reported, fixing some is not ("paying the debt down is silent").
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 |
147
204
 
148
- `paperlint lint` exits `1` when any check reports an error (as here, even though the summary line counts
149
- only the warnings in files), or when it checked no files at all (usually a wrong path). Warnings
150
- never fail the run unless you pass `--max-warnings <n>`. `--json` prints the findings as JSON.
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).
151
207
 
152
- ## Run it in CI
208
+ ## 🧩 Configuration
153
209
 
154
- `paperlint init` offers to write this GitHub Actions workflow step for you. By hand:
210
+ Two levels, one file name, both optional:
155
211
 
156
- ```yaml
157
- - uses: zernie/paperlint@v2.0.0
158
- with:
159
- paths: papers
212
+ ```
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
160
215
  ```
161
216
 
162
- Use the tag of the version you installed (`npm ls paperlint`): every npm release has
163
- a git tag of the same version, so the step runs the same code as your package. It runs
164
- `paperlint lint`. `paths` is required, and the step fails if it checked zero files, so a typo in the path
165
- shows up red. Optional inputs: `config`, `max-warnings` (default `-1`, no limit), `texcount` (default
166
- `true`), `working-directory`.
217
+ `papersDir` defaults to `papers`. If your papers live elsewhere, say so in a `paperlint.json`
218
+ beside your `package.json`:
167
219
 
168
- ## What the checks catch
220
+ ```json
221
+ { "papersDir": "docs/papers" }
222
+ ```
169
223
 
170
- | check | level | catches |
171
- | ------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------- |
172
- | `paper/stages` | error | a stage's PDF is missing, or it is not the same file any more (its size changed) |
173
- | `paper/source` | error | a stage has no frozen source file next to its PDF |
174
- | `paper/author-list` | warning | a stage is declared, but `PIPELINE-STATUS.md` does not record that the author list was checked |
175
- | `paper/research-question` | warning | the research question is missing from `PIPELINE-STATUS.md`, or the paper does not contain that sentence |
176
- | `paper/typography` | warning | more `§`, `.05`-style decimals, mixed `Fig.`/`Figure`, or references without a DOI or URL than you allowed |
177
- | `tex/future-promise` | warning | a camera-ready still says your code "will be released" |
178
- | `tex/acm-frontmatter-override` | error | an ACM paper overrides the template's title-page commands, so parts of page 1 go missing |
179
- | `review/findings-cause` | error | a review note lists several findings and names no cause for any of them |
180
- | `doc/fields` | warning | a review note's front matter is missing a field you require (off unless configured) |
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.
181
227
 
182
- Errors fail the run; warnings only print. What each check reads: [`docs/rules.md`](docs/rules.md).
183
- Checks that only some venues need are off until you turn them on:
184
- [`docs/optional-rules.md`](docs/optional-rules.md).
228
+ Every key: [`docs/configuration.md`](docs/configuration.md).
185
229
 
186
- ## Configuration
230
+ ### Another venue
187
231
 
188
- `paperlint init` writes the one required setting into `package.json`:
232
+ Write a preset in your repository, and extend it by path from the paper's `paperlint.json`
233
+ (`"extends": "../../venues/my-venue.jsonc"`):
189
234
 
190
- ```json
235
+ ```jsonc
191
236
  {
192
- "paperlint": {
193
- "papersDir": "papers"
194
- }
237
+ "extends": "paperlint:acm-sigconf",
238
+ "format": { "kinds": { "short": { "body_pages_max": 4 } } },
195
239
  }
196
240
  ```
197
241
 
198
- `papersDir` has no default, so the tool never checks a folder you did not choose: without it,
199
- `paperlint lint` stops and says so, unless you pass a path. An unknown key is an error. The optional
200
- settings, and how to use the checks inside your own ESLint setup, are in
201
- [`docs/configuration.md`](docs/configuration.md). TeX Live, its install location and the external
202
- programs the skills use are in [`docs/toolchain.md`](docs/toolchain.md).
242
+ The full shape: [`docs/rules.md`](docs/rules.md#writing-your-own-venue-preset).
203
243
 
204
- ## Claude Code (optional)
244
+ ## 🤖 Run it in CI
205
245
 
206
- `paperlint lint` needs only Node. If you use Claude Code, `paperlint init` also sets up two things:
246
+ `paperlint init` offers to write this workflow for you, pinned to the version you installed. By
247
+ hand:
207
248
 
208
- **Skills** — one per stage of writing a paper, from checking the idea through drafting, review,
209
- submission and camera-ready. They are linked into `.claude/skills/`. Start with `/paper-pipeline`;
210
- it routes to the rest. Some call Java or Python 3, which you install yourself.
249
+ ```yaml
250
+ - uses: zernie/paperlint@v3.0.0
251
+ with:
252
+ paths: papers
253
+ ```
211
254
 
212
- **Hooks** — written into `.claude/settings.json`. Commit that file so every clone gets them; a
213
- fresh clone needs `npm install` before they run.
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.
214
257
 
215
- | hook | blocks? | what it does |
216
- | -------------------- | ------- | ---------------------------------------------------------------- |
217
- | `paper-edit-guard` | yes | stops a shell command from writing to a paper file |
218
- | `paper-skills-nudge` | no | after a paper edit, shows the agent the pre-submission checklist |
219
- | `paper-status-gates` | no | after a paper edit, lists the stages that have not run yet |
258
+ ## ❓ FAQ
220
259
 
221
- **Know this about `paper-edit-guard`:** if the `paperlint` key in `package.json`
222
- cannot be read (the file is missing, or has merge-conflict markers), it blocks **every** shell
223
- command in Claude Code, not only paper ones, until the key is readable again. Fix `package.json`
224
- with a normal file edit — those are not blocked.
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.
225
263
 
226
- ## What it does not do
264
+ **Will `new` or `init` overwrite my files?**
265
+ No. They only add what is missing, and `init` keeps a `papersDir` you already declared.
227
266
 
228
- - **It does not install software behind your back.** `paperlint init` installs nothing; TeX Live comes
229
- only from `paperlint toolchain` or when you answer yes in `paperlint build`. On Windows, install TeX Live
230
- yourself.
231
- - **It does not guess what to check.** There is no default papers directory.
232
- - **It does not overwrite your files.** `paperlint new` and `paperlint init` only add what is missing.
233
- - **It does not run your build script.** `paperlint build` compiles the paper itself.
234
- - **It does not write or grade the paper.** `paperlint lint` checks records and a few mechanical
235
- mistakes; judging the writing is what the optional skills are for.
236
- - **New papers are LaTeX.** Markdown papers (`paper.md`) are still read but deprecated
237
- ([#57](https://github.com/zernie/paperlint/issues/57)).
238
- - **No Yarn Plug'n'Play.** npm and pnpm are supported
239
- ([`docs/install.md`](docs/install.md#package-managers)).
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.
240
270
 
241
- ## Contributing
271
+ **Do I need TeX Live just to lint?**
272
+ No. `paperlint lint` needs only Node. `paperlint build` needs TeX Live.
242
273
 
243
- See [`CONTRIBUTING.md`](CONTRIBUTING.md): how the package is tested, the one command that runs
244
- every check, and how releases work: the pull-request title decides the version (`feat:` is a
245
- minor release, `fix:` a patch).
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.
246
277
 
247
- ## License
278
+ **Several papers for different venues in one repo?**
279
+ Yes. Each paper names its own venue in its own `paperlint.json`.
280
+
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`.
284
+
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)).
289
+
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).
248
293
 
249
- MIT.
294
+ ## 📚 Docs
295
+
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
302
+
303
+ ## License
250
304
 
251
- **banal is not part of this package.** banal, the page-geometry script from
252
- [HotCRP](https://github.com/kohler/hotcrp) (Geoffrey M. Voelker, Eddie Kohler), is licensed
253
- GPL-2.0-or-later. paperlint does not contain, copy or modify it: `paperlint toolchain` downloads it from HotCRP
254
- at a pinned commit, checks its sha256, and paperlint runs it as a separate program (`perl banal …`),
255
- 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"}