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.
- package/CLAUDE.md +6 -1
- package/CONTRIBUTING.md +21 -0
- package/README.md +231 -180
- package/action.yml +4 -6
- package/dist/adapters/references/index.d.ts +13 -0
- package/dist/adapters/references/index.d.ts.map +1 -0
- package/dist/adapters/references/index.js +55 -0
- package/dist/adapters/references/index.js.map +1 -0
- package/dist/adapters/references/reach.io.d.ts +3 -0
- package/dist/adapters/references/reach.io.d.ts.map +1 -0
- package/dist/adapters/references/reach.io.js +14 -0
- package/dist/adapters/references/reach.io.js.map +1 -0
- package/dist/build-engine.d.ts.map +1 -1
- package/dist/build-engine.js +5 -2
- package/dist/build-engine.js.map +1 -1
- package/dist/build.d.ts +16 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +40 -5
- package/dist/build.js.map +1 -1
- package/dist/cli.d.ts +37 -20
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +377 -251
- package/dist/cli.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +37 -52
- package/dist/doctor.js.map +1 -1
- package/dist/facts-file.d.ts +33 -9
- package/dist/facts-file.d.ts.map +1 -1
- package/dist/facts-file.js +117 -17
- package/dist/facts-file.js.map +1 -1
- package/dist/hooks-settings.d.ts +0 -21
- package/dist/hooks-settings.d.ts.map +1 -1
- package/dist/hooks-settings.js +17 -135
- package/dist/hooks-settings.js.map +1 -1
- package/dist/init.d.ts +40 -19
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +144 -109
- package/dist/init.js.map +1 -1
- package/dist/link-skills.d.ts +1 -1
- package/dist/link-skills.d.ts.map +1 -1
- package/dist/link-skills.js +11 -29
- package/dist/link-skills.js.map +1 -1
- package/dist/new-paper.d.ts +2 -2
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +6 -2
- package/dist/new-paper.js.map +1 -1
- package/dist/paper-files.d.ts +63 -0
- package/dist/paper-files.d.ts.map +1 -0
- package/dist/paper-files.js +69 -0
- package/dist/paper-files.js.map +1 -0
- package/dist/paper-settings.d.ts +40 -0
- package/dist/paper-settings.d.ts.map +1 -0
- package/dist/paper-settings.js +144 -0
- package/dist/paper-settings.js.map +1 -0
- package/dist/ports/check-references.d.ts +31 -0
- package/dist/ports/check-references.d.ts.map +1 -0
- package/dist/ports/check-references.js +7 -0
- package/dist/ports/check-references.js.map +1 -0
- package/dist/presets.d.ts +84 -0
- package/dist/presets.d.ts.map +1 -0
- package/dist/presets.js +184 -0
- package/dist/presets.js.map +1 -0
- package/dist/reference-rules.d.ts +47 -0
- package/dist/reference-rules.d.ts.map +1 -0
- package/dist/reference-rules.js +156 -0
- package/dist/reference-rules.js.map +1 -0
- package/dist/references.d.ts +36 -0
- package/dist/references.d.ts.map +1 -0
- package/dist/references.js +91 -0
- package/dist/references.js.map +1 -0
- package/dist/rules-config.d.ts +11 -2
- package/dist/rules-config.d.ts.map +1 -1
- package/dist/rules-config.js +25 -11
- package/dist/rules-config.js.map +1 -1
- package/dist/structure.d.ts.map +1 -1
- package/dist/structure.js +3 -2
- package/dist/structure.js.map +1 -1
- package/dist/tex-requirements.d.ts +63 -7
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +91 -24
- package/dist/tex-requirements.js.map +1 -1
- package/dist/types.d.ts +20 -18
- package/dist/types.d.ts.map +1 -1
- package/dist/venue-rules.d.ts +109 -0
- package/dist/venue-rules.d.ts.map +1 -0
- package/dist/venue-rules.js +389 -0
- package/dist/venue-rules.js.map +1 -0
- package/docs/configuration.md +152 -77
- package/docs/e2e.md +1 -1
- package/docs/install.md +25 -40
- package/docs/optional-rules.md +26 -24
- package/docs/rules.md +181 -15
- package/eslint-rules/bib-reachable-entry.mjs +75 -0
- package/eslint-rules/latex-language.mjs +29 -1
- package/eslint-rules/paper-stages.harness.mjs +1 -117
- package/eslint-rules/paper-stages.mjs +0 -120
- package/eslint-rules/paper-stages.mutations.mjs +0 -76
- package/eslint-rules/paper-typography.mjs +267 -192
- package/eslint-rules/paper-typography.test.mjs +297 -0
- package/eslint-rules/papers.harness.mjs +8 -12
- package/eslint-rules/papers.mjs +17 -27
- package/eslint-rules/pdf-last-page-balance.mjs +23 -5
- package/eslint-rules/review-frontmatter.mjs +118 -0
- package/eslint-rules/review-frontmatter.schema.json +36 -0
- package/eslint-rules/review-frontmatter.test.mjs +118 -0
- package/eslint-rules/sibling-frontmatter.mjs +19 -0
- package/eslint-rules/sibling-frontmatter.schema.json +14 -0
- package/eslint.config.mjs +0 -15
- package/fixtures/build-e2e/acmart/paperlint.json +1 -0
- package/fixtures/real-markdown-paper/baseline.json +6 -7
- package/hooks/hooks.harness.mjs +39 -98
- package/hooks/hooks.mutations.mjs +6 -6
- package/hooks/paper-edit-guard.hook.d.mts +2 -3
- package/hooks/paper-edit-guard.hook.mjs +31 -42
- package/hooks/paper-skills-nudge.hook.mjs +21 -19
- package/hooks/paper-status-gates.hook.mjs +21 -19
- package/hooks/paper-status-gates.sh +8 -13
- package/lib/paper-config.d.mts +15 -18
- package/lib/paper-config.harness.mjs +18 -35
- package/lib/paper-config.mjs +90 -81
- package/lib/paper-config.mutations.mjs +2 -2
- package/lib/skill-checks.mjs +1 -1
- package/lib/skill-trigger-cases.harness.mjs +9 -8
- package/lib/skill-trigger-cases.mjs +7 -15
- package/package.json +1 -1
- package/scripts/eslint-report-guard.mjs +1 -1
- package/scripts/harness-api.frozen.json +0 -3
- package/scripts/mutation-batteries.frozen.json +4 -13
- package/skills/analyze-sibling-paper/SKILL.md +16 -12
- package/skills/analyze-sibling-paper/SKILL.md.spec.ts +15 -11
- package/skills/cold-read-diff/SKILL.md +20 -1
- package/skills/cold-read-diff/SKILL.md.spec.ts +19 -0
- package/skills/find-venue/SKILL.md +5 -1
- package/skills/find-venue/SKILL.md.spec.ts +4 -0
- package/skills/grade-paper-writing/SKILL.md +20 -1
- package/skills/grade-paper-writing/SKILL.md.spec.ts +19 -0
- package/skills/paper-pipeline/PIPELINE-MAP.md +14 -4
- package/skills/paper-pipeline/SKILL.md +4 -3
- package/skills/paper-pipeline/SKILL.md.spec.ts +3 -2
- package/skills/paper-pipeline/scripts/consumer.d.mts +0 -1
- package/skills/paper-pipeline/scripts/consumer.harness.mjs +8 -12
- package/skills/paper-pipeline/scripts/consumer.mjs +19 -33
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +6 -4
- package/skills/paper-pipeline/scripts/run-mechanical.mjs +2 -11
- package/skills/plan-paper-timeline/SKILL.md +2 -2
- package/skills/plan-paper-timeline/SKILL.md.spec.ts +1 -1
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +1 -1
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.mutations.mjs +2 -2
- package/skills/render-paper/check-render.sh +2 -3
- package/skills/render-paper/extract-pdf-facts.harness.mjs +5 -5
- package/skills/render-paper/extract-pdf-facts.mjs +4 -4
- package/skills/render-paper/render-paper.harness.mjs +2 -2
- package/skills/study-accepted-papers/SKILL.md +2 -1
- package/skills/study-accepted-papers/SKILL.md.spec.ts +1 -0
- package/skills/submit-paper/SKILL.md +9 -1
- package/skills/submit-paper/SKILL.md.spec.ts +8 -0
- package/skills/submit-paper/references/venues/acm-sigconf.jsonc +86 -0
- package/skills/submit-paper/references/venues/agenticdev.jsonc +34 -102
- package/skills/submit-paper/references/venues/aisec.jsonc +21 -82
- package/skills/submit-paper/references/venues/realm.jsonc +45 -43
- package/skills/submit-paper/references/venues/tex-base.jsonc +1 -1
- package/skills/submit-paper/references/venues/venue-profile.schema.json +119 -29
- package/skills/verify-citations/SKILL.md +10 -1
- package/skills/verify-citations/SKILL.md.spec.ts +9 -0
- package/skills/verify-citations/scripts/bib-authors.mjs +48 -26
- package/src/adapters/references/index.ts +102 -0
- package/src/adapters/references/reach.io.ts +12 -0
- package/src/build-engine.ts +10 -2
- package/src/build.harness.mjs +31 -22
- package/src/build.ts +65 -8
- package/src/cli.harness.mjs +151 -294
- package/src/cli.mutations.mjs +35 -44
- package/src/cli.ts +457 -288
- package/src/doctor.harness.mjs +41 -84
- package/src/doctor.mutations.mjs +9 -9
- package/src/doctor.ts +41 -61
- package/src/facts-file.test.ts +47 -7
- package/src/facts-file.ts +145 -24
- package/src/hooks-settings.harness.mjs +7 -92
- package/src/hooks-settings.mutations.mjs +2 -17
- package/src/hooks-settings.ts +18 -162
- package/src/init.test.ts +206 -0
- package/src/init.ts +193 -159
- package/src/link-skills.harness.mjs +1 -38
- package/src/link-skills.mutations.mjs +9 -5
- package/src/link-skills.ts +10 -34
- package/src/lint-fix.test.ts +120 -0
- package/src/new-paper.harness.mjs +9 -6
- package/src/new-paper.test.ts +133 -0
- package/src/new-paper.ts +7 -2
- package/src/paper-files.test.ts +290 -0
- package/src/paper-files.ts +144 -0
- package/src/paper-settings-commands.test.ts +274 -0
- package/src/paper-settings.test.ts +249 -0
- package/src/paper-settings.ts +208 -0
- package/src/ports/check-references.ts +28 -0
- package/src/presets.test.ts +236 -0
- package/src/presets.ts +297 -0
- package/src/reference-rules.ts +229 -0
- package/src/references.test.ts +209 -0
- package/src/references.ts +138 -0
- package/src/rules-config.harness.mjs +24 -8
- package/src/rules-config.ts +30 -13
- package/src/structure.harness.mjs +6 -4
- package/src/structure.ts +3 -2
- package/src/tex-requirements.harness.mjs +11 -17
- package/src/tex-requirements.ts +179 -29
- package/src/types.ts +30 -18
- package/src/venue-rules.test.ts +554 -0
- package/src/venue-rules.ts +571 -0
- package/templates/paper/paperlint.json +4 -0
- package/eslint-rules/doc-fields.harness.mjs +0 -336
- package/eslint-rules/doc-fields.mjs +0 -186
- package/eslint-rules/doc-fields.mutations.mjs +0 -96
- package/eslint-rules/paper-typography.harness.mjs +0 -291
- package/eslint-rules/paper-typography.mutations.mjs +0 -131
- package/eslint-rules/review-findings-cause.harness.mjs +0 -228
- package/eslint-rules/review-findings-cause.mjs +0 -135
- package/eslint-rules/review-findings-cause.mutations.mjs +0 -72
- package/fixtures/build-e2e/acmart/venue.json +0 -1
- package/fixtures/paper-stages/authors-ran/PIPELINE-STATUS.md +0 -16
- package/fixtures/paper-stages/marker-in-prose/PIPELINE-STATUS.md +0 -17
- package/fixtures/review-findings-cause/clean.md +0 -17
- package/fixtures/review-findings-cause/defect.md +0 -14
- package/fixtures/review-findings-cause/old-debt.md +0 -14
- package/fixtures/review-findings-cause/quiet-in-fence.md +0 -16
package/docs/configuration.md
CHANGED
|
@@ -1,103 +1,175 @@
|
|
|
1
1
|
# Configuration reference
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
paperlint reads one file name at two levels, `paperlint.json`, with one schema. Both files are
|
|
4
|
+
optional: a project with its papers in `papers/` and no special wishes needs neither.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
```
|
|
7
|
+
paperlint.json the PROJECT (optional): where the papers are, the project's rules,
|
|
8
|
+
defaults for every paper
|
|
9
|
+
papers/
|
|
10
|
+
my-paper/
|
|
11
|
+
paper.tex
|
|
12
|
+
PIPELINE-STATUS.md
|
|
13
|
+
paperlint.json THIS PAPER: its venue preset, its kind, its own rules — `paperlint new` writes it
|
|
14
|
+
venues/usenix-sec.jsonc (optional, your own) a VENUE PRESET: format, page limits, TeX packages, rules
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The shipped presets (`paperlint:<name>`) live in the package, under
|
|
18
|
+
`skills/submit-paper/references/venues/`: `acm-sigconf`, `agenticdev`, `aisec`, `realm`.
|
|
19
|
+
|
|
20
|
+
## The root `paperlint.json`
|
|
21
|
+
|
|
22
|
+
It sits beside your `package.json`. Without it every setting has its default.
|
|
8
23
|
|
|
9
24
|
```json
|
|
10
25
|
{
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"rules": [
|
|
20
|
-
{
|
|
21
|
-
"files": ["papers/agenticdev-2026/**"],
|
|
22
|
-
"rules": { "pdf/last-page-balance": ["error", { "tolerancePt": 120 }] }
|
|
23
|
-
}
|
|
24
|
-
]
|
|
25
|
-
}
|
|
26
|
+
"papersDir": "docs/papers",
|
|
27
|
+
"extends": "paperlint:acm-sigconf",
|
|
28
|
+
"rules": [
|
|
29
|
+
{
|
|
30
|
+
"files": ["docs/papers/old-draft/**"],
|
|
31
|
+
"rules": { "paper/section-word": "off" }
|
|
32
|
+
}
|
|
33
|
+
]
|
|
26
34
|
}
|
|
27
35
|
```
|
|
28
36
|
|
|
29
|
-
| key
|
|
30
|
-
|
|
|
31
|
-
| `papersDir`
|
|
32
|
-
| `structure`
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
`
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
`
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
`
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
37
|
+
| key | default | what it is |
|
|
38
|
+
| ----------- | ---------- | ------------------------------------------------------------------------------------------ |
|
|
39
|
+
| `papersDir` | `"papers"` | the directory your papers live in, relative to this file. One string or a list. Root only. |
|
|
40
|
+
| `structure` | see below | which files every paper directory must contain. `false` turns it off. Root only. |
|
|
41
|
+
| `rules` | none | rule overrides — see [below](#the-rules-key-turning-rules-on-and-off) |
|
|
42
|
+
| `extends` | none | the venue preset for every paper that names none |
|
|
43
|
+
| `kind` | none | the kind of paper for every paper that names none |
|
|
44
|
+
| `pdf` | none | where the built PDF is, relative to each paper, when it is not `paper.pdf` |
|
|
45
|
+
| `$comment` | — | a note for humans (JSON Schema's comment keyword); ignored |
|
|
46
|
+
|
|
47
|
+
The skill scripts read a few more root keys — `ledger`, `scripts`, `timezone`, `contactEmail`,
|
|
48
|
+
`citeChecks`, `triggerCases` — documented with the skills that use them.
|
|
49
|
+
|
|
50
|
+
**`paperlint lint` finds the file** by walking up from the current directory to the nearest
|
|
51
|
+
`paperlint.json` that is not a paper's own (a paper's sits beside its `paper.tex`); if there is
|
|
52
|
+
none, the project root is the nearest directory with a `package.json`. It prints which file it
|
|
53
|
+
found. `--config <file>` names another file of the same shape.
|
|
54
|
+
|
|
55
|
+
**No papers where `papersDir` points is an error**, not a clean run:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
no papers in papers/ — create one with `npx paperlint new <name>`, or set "papersDir" in paperlint.json if your papers live elsewhere
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`paperlint init` writes the root file only when something differs from the defaults: it measures
|
|
62
|
+
where your papers are, and writes `{ "papersDir": … }` only when that is not `papers`. A
|
|
63
|
+
`papersDir` already declared is kept, and nothing is measured or asked.
|
|
64
|
+
|
|
65
|
+
**Under `papersDir`, only the files paperlint's own rules are written for are linted:**
|
|
66
|
+
`PIPELINE-STATUS.md`, `paper.md`, `draft.md`, `paper.tex`, `reviews/*.md` and `siblings/*.md`.
|
|
67
|
+
Everything else — a paper's `repro/` scripts, vendored JavaScript, data files — is never handed to
|
|
68
|
+
ESLint, so it cannot fail the run. A file you name on the command line that is not one of these is
|
|
69
|
+
refused by name. `node_modules/`, `.git/` and `<papers>/.template/` are skipped.
|
|
70
|
+
|
|
71
|
+
## A paper's `paperlint.json`
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"extends": "paperlint:aisec",
|
|
76
|
+
"kind": "research",
|
|
77
|
+
"rules": { "pdf/body-size": "off" }
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The same keys as the root file, minus the project-only ones (`papersDir`, `structure` and the
|
|
82
|
+
skills' keys), which are refused here by name. **It merges over the root file:** its `extends`,
|
|
83
|
+
`kind` and `pdf` win; one it does not set comes from the root.
|
|
84
|
+
|
|
85
|
+
| key | what it is |
|
|
86
|
+
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
87
|
+
| `extends` | the venue preset the built PDF is judged against: `paperlint:<name>` (shipped) or `./path` / `../path` (your own, relative to this file) — [`rules.md`](rules.md#checks-against-the-venue) |
|
|
88
|
+
| `kind` | the kind of paper (`short`, `research`, …) whose page limit applies |
|
|
89
|
+
| `pdf` | where the built PDF is, relative to the paper, when it is not `paper.pdf` |
|
|
90
|
+
| `rules` | rule overrides for this paper alone — `{ "<rule>": "<severity>" }`, or blocks with globs relative to the paper |
|
|
91
|
+
| `$comment` | a note for humans; ignored |
|
|
92
|
+
|
|
93
|
+
`paperlint new` writes this file from the template (`templates/paper/paperlint.json`, or your
|
|
94
|
+
`<papers>/.template/paperlint.json` if you keep one), with `"extends": null` — no venue chosen yet —
|
|
95
|
+
and a `$comment` saying what goes there. Until `extends` names a preset (here or in the root file),
|
|
96
|
+
`paperlint lint` gives that paper one warning, `pdf/measured`: "this paper names no venue preset
|
|
97
|
+
yet … set "extends" in papers/my-paper/paperlint.json".
|
|
98
|
+
|
|
99
|
+
**Any other key is an error**, in either file, named in the message: `paperlint.json: unknown key
|
|
100
|
+
"papersdir"`. A misspelt key would otherwise read as "not set", and the setting you meant would
|
|
101
|
+
silently do nothing. The key list is `SETTINGS_KEYS` in `lib/paper-config.mjs`.
|
|
102
|
+
|
|
103
|
+
**Where a paper's rules come from, in order — a later one wins, rule by rule:** paperlint's own
|
|
104
|
+
configuration → the preset chain's `rules`, from the root preset to the one the paper extends →
|
|
105
|
+
the root `paperlint.json`'s `rules` → the paper's own `rules`. So a venue can turn a rule on, the
|
|
106
|
+
project can change that for every paper, and one paper can still change it for itself. Only rules
|
|
107
|
+
paperlint ships may be named, at every level.
|
|
108
|
+
|
|
109
|
+
## Records in frontmatter, checked by shipped JSON Schemas
|
|
110
|
+
|
|
111
|
+
Two kinds of file under a paper keep a record in their YAML frontmatter, and paperlint validates
|
|
112
|
+
each against a JSON Schema it ships (`eslint-rules/*.schema.json`):
|
|
113
|
+
|
|
114
|
+
- **a review** under `reviews/` (`review/frontmatter`, error) — its findings, each with a status and,
|
|
115
|
+
when open, the pipeline cause that let it through:
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
---
|
|
119
|
+
findings:
|
|
120
|
+
- id: 1
|
|
121
|
+
status: open # open | fixed | wontfix
|
|
122
|
+
cause: missing-skill # skill-defect | missing-skill | hook | rule — required when open
|
|
123
|
+
---
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
A review with no `findings` key is not checked for findings.
|
|
127
|
+
|
|
128
|
+
- **a sibling card** under `siblings/` (`sibling/frontmatter`, warn; `siblings/README.md` is the index,
|
|
129
|
+
not a card) — how much of the competing paper was actually read: `read: full | abstract | none`. A
|
|
130
|
+
card without it, or without frontmatter, is a finding.
|
|
70
131
|
|
|
71
132
|
## The `rules` key: turning rules on and off
|
|
72
133
|
|
|
73
|
-
`rules`
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
134
|
+
`rules` takes two shapes, in either file:
|
|
135
|
+
|
|
136
|
+
- **`{ "<rule>": "<severity>" }`** — for every paper file in the file's scope: every paper in the
|
|
137
|
+
root file, that one paper in a paper's file. The simple form, with no glob to get wrong.
|
|
138
|
+
- **a list of blocks** in ESLint's own
|
|
139
|
+
[flat-config shape](https://eslint.org/docs/latest/use/configure/configuration-files), limited to
|
|
140
|
+
the three keys that make sense in JSON — `files`, `ignores` and `rules` — for a rule across some
|
|
141
|
+
papers but not others.
|
|
142
|
+
|
|
143
|
+
paperlint applies them **after** its own configuration, so, as in ESLint, a later one wins: they
|
|
144
|
+
can turn on a rule that is off by default, or change the severity of one that is on.
|
|
78
145
|
|
|
79
146
|
```json
|
|
80
147
|
"rules": [
|
|
81
148
|
{
|
|
82
|
-
"files": ["papers
|
|
83
|
-
"rules": { "pdf/
|
|
149
|
+
"files": ["papers/**"],
|
|
150
|
+
"rules": { "pdf/body-size": "off" }
|
|
84
151
|
},
|
|
85
152
|
{
|
|
86
153
|
"files": ["papers/old-draft/**"],
|
|
87
|
-
"rules": { "paper/
|
|
154
|
+
"rules": { "paper/section-word": "off" }
|
|
88
155
|
}
|
|
89
156
|
]
|
|
90
157
|
```
|
|
91
158
|
|
|
92
|
-
- **`files` and `ignores` are globs relative to the file that holds
|
|
93
|
-
|
|
159
|
+
- **`files` and `ignores` are globs relative to the file that holds them** — the project root for
|
|
160
|
+
the root file, the paper for a paper's — exactly as ESLint resolves them relative to its config file,
|
|
94
161
|
whatever directory you run `paperlint lint` from. A block without `files` applies to every linted file.
|
|
95
162
|
A pattern ending in `/**` is the usual way to name one paper.
|
|
163
|
+
- **Each rule reaches only the files it is written for.** A block with `"files": ["papers/**"]` and
|
|
164
|
+
`"rules": { "paper/source": "warn" }` turns `paper/source` on for every `PIPELINE-STATUS.md` under
|
|
165
|
+
`papers/`; `paper/section-word` in the same block lands on every `paper.md`, `draft.md` and
|
|
166
|
+
`paper.tex`. You do not need to know which file a rule reads: your `files` narrow where it runs,
|
|
167
|
+
never widen it. The [rule tables](rules.md) name each rule's file.
|
|
96
168
|
- **A rule entry** is a severity (`"off"`, `"warn"`, `"error"`, or `0`/`1`/`2`), or a list whose
|
|
97
169
|
first element is a severity and the rest are the rule's options.
|
|
98
170
|
- **Only rules paperlint ships can be named** — the ones in [`docs/rules.md`](rules.md) and
|
|
99
171
|
[`docs/optional-rules.md`](optional-rules.md). A rule id paperlint does not ship, a bad severity, a
|
|
100
|
-
`rules`
|
|
172
|
+
`rules` of another shape, or a block key other than `files`, `ignores` and `rules` stops the run
|
|
101
173
|
with a message naming the exact key, before anything is linted.
|
|
102
174
|
- **An optional rule you turned on must reach a paper.** If no linted `paper.tex` gets the rule —
|
|
103
175
|
usually a `files` glob with a typo — `paperlint lint` fails and says so: a rule that never runs
|
|
@@ -116,7 +188,7 @@ and the real findings leave with it.
|
|
|
116
188
|
|
|
117
189
|
```json
|
|
118
190
|
"structure": {
|
|
119
|
-
"markers": ["PIPELINE-STATUS.md", "paper.tex", "paper.md", "
|
|
191
|
+
"markers": ["PIPELINE-STATUS.md", "paper.tex", "paper.md", "paperlint.json"],
|
|
120
192
|
"require": ["PIPELINE-STATUS.md"],
|
|
121
193
|
"requireOneOf": [["paper.tex", "paper.md"]],
|
|
122
194
|
"ignore": []
|
|
@@ -173,11 +245,12 @@ papers/my-paper
|
|
|
173
245
|
|
|
174
246
|
The build does **not** judge the layout. A balanced last page, a page limit, the fonts a venue
|
|
175
247
|
wants — those are verdicts about the finished PDF, and they belong to lint rules that can be
|
|
176
|
-
turned on per venue, given a severity and suppressed with a reason
|
|
248
|
+
turned on per venue, given a severity and suppressed with a reason: the `pdf/` venue rules
|
|
249
|
+
([`rules.md`](rules.md#checks-against-the-venue)) and the optional `pdf/last-page-balance`. paperlint once searched for a
|
|
177
250
|
`\balance` position itself and failed the build when none worked; that was removed on
|
|
178
251
|
2026-09-24.
|
|
179
252
|
|
|
180
|
-
The class and its options and the venue in `
|
|
253
|
+
The class and its options and the venue in `paperlint.json` are read from the paper and shown in the
|
|
181
254
|
plan; later steps decide from them whether they apply.
|
|
182
255
|
|
|
183
256
|
**`paper.pdf` is deleted before anything runs**, for every targeted paper — before the TeX Live is
|
|
@@ -211,7 +284,7 @@ having run on it, because a missing build read as nothing to do.
|
|
|
211
284
|
|
|
212
285
|
⚠️ **A `build.sh` or `repro/build-submission.sh` in the paper directory is IGNORED.** Earlier
|
|
213
286
|
versions ran it; `paperlint build` now says one line — `build.sh is ignored — paperlint builds the paper
|
|
214
|
-
itself` — and builds the paper itself.
|
|
287
|
+
itself` — and builds the paper itself.
|
|
215
288
|
Why: [#59](https://github.com/zernie/paperlint/issues/59).
|
|
216
289
|
|
|
217
290
|
## Using the rules from an existing ESLint config
|
|
@@ -225,7 +298,9 @@ the exact config the CLI uses, so the shortest path is:
|
|
|
225
298
|
// eslint.config.mjs
|
|
226
299
|
import { buildConfig } from "paperlint/bin/paperlint.mjs";
|
|
227
300
|
import { texLanguage } from "paperlint/eslint-rules/latex-language.mjs";
|
|
228
|
-
export default buildConfig({
|
|
301
|
+
export default buildConfig({}, texLanguage);
|
|
229
302
|
```
|
|
230
303
|
|
|
231
|
-
|
|
304
|
+
That config is the whole config for your papers: it starts with a global ignore of every file its
|
|
305
|
+
rules are not written for, so `eslint .` with it lints only the paper files. Do not spread it into a
|
|
306
|
+
config that also lints your JavaScript — that code would be ignored.
|
package/docs/e2e.md
CHANGED
|
@@ -35,7 +35,7 @@ temporary tree, **under npm and under pnpm separately**, and drives the installe
|
|
|
35
35
|
|
|
36
36
|
- the install itself finishes
|
|
37
37
|
- `paperlint --help` answers with zero
|
|
38
|
-
- `paperlint init`
|
|
38
|
+
- `paperlint init` takes the default papers directory and writes no `paperlint.json` for it
|
|
39
39
|
- `paperlint init` finishes with zero — its doctor found no discrepancy
|
|
40
40
|
- `paperlint init` wires the hooks into `.claude/settings.json` — the same commands `hooks.json`
|
|
41
41
|
publishes, once each — and says they need `npm install` in a fresh clone; a second `init`
|
package/docs/install.md
CHANGED
|
@@ -14,21 +14,6 @@ npx paperlint init
|
|
|
14
14
|
Two commands in one terminal, in this order: `init` links the skills and hooks to the copy in
|
|
15
15
|
your project's `node_modules`, so it needs the install first.
|
|
16
16
|
|
|
17
|
-
**Upgrading from `research-paper-pipeline`** (the package's name before 2.0.0):
|
|
18
|
-
`npm rm research-paper-pipeline && npm i -D paperlint`, then `npx paperlint init`. It moves the
|
|
19
|
-
`"research-paper-pipeline"` key in `package.json` to `"paperlint"`, and replaces the hook commands
|
|
20
|
-
and skill links that pointed into the old package. Until then the old key is still read, with a
|
|
21
|
-
warning.
|
|
22
|
-
|
|
23
|
-
**Upgrading from 2.0.0**: 2.0.0 wrote hook commands that run `node_modules/paperlint/bin/rpp.mjs`,
|
|
24
|
-
a file later versions do not ship, so those hooks stop running. `npx paperlint doctor` names them;
|
|
25
|
-
`npx paperlint init` replaces them and keeps your own commands in the same matcher. The TeX Live
|
|
26
|
-
and banal caches moved from `~/.cache/rpp/` to `~/.cache/paperlint/`, so `npx paperlint toolchain`
|
|
27
|
-
downloads TeX Live once more (delete the old directory afterwards), and the environment variables
|
|
28
|
-
are now `PAPERLINT_TEXLIVE_DIR`, `PAPERLINT_BANAL_DIR` and `PAPERLINT_CTAN_MIRROR` — the `RPP_*`
|
|
29
|
-
names are no longer read. A leftover `rpp.json` is ignored; its settings belong under the
|
|
30
|
-
`"paperlint"` key of `package.json`.
|
|
31
|
-
|
|
32
17
|
The npm package carries everything: the `paperlint` command, the ESLint rules, the Claude Code skills
|
|
33
18
|
and hooks, and the scripts the skills run. External programs are separate:
|
|
34
19
|
|
|
@@ -43,15 +28,18 @@ and hooks, and the scripts the skills run. External programs are separate:
|
|
|
43
28
|
|
|
44
29
|
In order:
|
|
45
30
|
|
|
46
|
-
1. **Finds the papers directory**, or asks for it
|
|
47
|
-
|
|
48
|
-
|
|
31
|
+
1. **Finds the papers directory**, or asks for it when several directories look like one.
|
|
32
|
+
2. **Declares it**, in a root `paperlint.json`, only when it is not the default `papers`. A project
|
|
33
|
+
on the defaults gets no config file at all.
|
|
49
34
|
3. **Links each shipped skill** into `.claude/skills/<name>`, where Claude Code looks for skills.
|
|
50
35
|
4. **Writes the hook commands** into `.claude/settings.json`, beside your own entries.
|
|
51
36
|
5. **Offers a GitHub Actions workflow**, pinned to the release tag of the version you installed.
|
|
52
37
|
6. **Offers a first paper** (`paperlint new`) if the papers directory has none.
|
|
53
|
-
7. **
|
|
54
|
-
|
|
38
|
+
7. **Offers TeX Live** for `paperlint build` (`paperlint toolchain`, ~270 MB, ~3 min, once), with
|
|
39
|
+
the size in the question and NO as the default. Without a terminal it asks nothing and lists the
|
|
40
|
+
command as the next step. `paperlint lint` needs no TeX.
|
|
41
|
+
8. **Reports missing external programs** and the command that installs each. It installs nothing.
|
|
42
|
+
9. **Runs `paperlint doctor`** and exits with its verdict.
|
|
55
43
|
|
|
56
44
|
It asks only what it cannot guess or what costs something. A human at a terminal is asked; an
|
|
57
45
|
agent, CI or `--yes` takes the defaults, and `init` prints which default it took. An unanswered
|
|
@@ -61,23 +49,23 @@ question (Ctrl+D) also takes the default.
|
|
|
61
49
|
|
|
62
50
|
| what | where | when |
|
|
63
51
|
| ------------------------------------------------------------------ | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
64
|
-
|
|
|
52
|
+
| `{ "papersDir": … }` naming your papers directory | `paperlint.json` | only when it is not the default `papers` |
|
|
65
53
|
| the three hook commands, merged in beside your own entries | `.claude/settings.json` | by default. A human at a terminal is asked [Y/n]; an agent, CI or `--yes` gets YES; `--no-hooks` skips. Hand-wired under another spelling: nothing written, so nothing runs twice |
|
|
66
54
|
| a GitHub Actions workflow, pinned to `@v<installed version>` | `.github/workflows/` | only if you say yes; it asks once, and only when a human is at a terminal (stdin and stdout, no `CI`, no `--yes`) |
|
|
67
55
|
| a first paper, via `paperlint new` | `<papers>/<name>/` | only when the papers directory holds none: asked of a human at a terminal, otherwise only with `--paper <name>` |
|
|
68
56
|
| one relative symlink per shipped skill, into the installed package | `.claude/skills/<name>` | always — except where that name is already taken (a directory, a file, a link elsewhere): that entry is left as it is and named in the report |
|
|
69
57
|
|
|
70
|
-
It installs no software and touches nothing else. Commit `.claude/settings.json` so every clone
|
|
58
|
+
It installs no software you did not say yes to, and touches nothing else. Commit `.claude/settings.json` so every clone
|
|
71
59
|
gets the hooks; the hook commands run files inside `node_modules`, so a fresh clone needs
|
|
72
60
|
`npm install` first.
|
|
73
61
|
|
|
74
62
|
## Why it is shaped this way
|
|
75
63
|
|
|
76
|
-
**One
|
|
77
|
-
config; it can only read a path it can spell, and
|
|
78
|
-
|
|
79
|
-
`lib/skill-trigger-cases.mjs` and `skills/paper-pipeline/scripts/consumer.mjs
|
|
80
|
-
|
|
64
|
+
**One file, at the project root.** A hook cannot import code or walk up a tree looking for a
|
|
65
|
+
config; it can only read a path it can spell, and `$CLAUDE_PROJECT_DIR/paperlint.json` is one. The
|
|
66
|
+
same file is read by the CLI, the three hooks, `eslint-rules/papers.mjs`,
|
|
67
|
+
`lib/skill-trigger-cases.mjs` and `skills/paper-pipeline/scripts/consumer.mjs`. It is optional:
|
|
68
|
+
absent, every reader takes the same defaults.
|
|
81
69
|
|
|
82
70
|
**Nothing runs at install time.** No postinstall script and no automatic TeX download. npm's rule
|
|
83
71
|
is that _"the only valid use of install or preinstall scripts is for compilation"_; husky removed
|
|
@@ -110,9 +98,7 @@ resolve.
|
|
|
110
98
|
nothing while guarding and says nothing while watching a directory that does not exist, so
|
|
111
99
|
"installed" and "protecting you" look the same from outside. `doctor` prints the papers directory
|
|
112
100
|
`paperlint lint` resolves and the one the hooks resolve, whether they match, whether the hooks are wired
|
|
113
|
-
(once, twice, or not at all), and which external programs are missing.
|
|
114
|
-
project still enables the old `research-paper-pipeline` plugin, because plugin plus settings would
|
|
115
|
-
run every hook twice. A plugin installed at user scope is outside what it can read, and it says so.
|
|
101
|
+
(once, twice, or not at all), and which external programs are missing.
|
|
116
102
|
|
|
117
103
|
### How comparable tools install
|
|
118
104
|
|
|
@@ -166,13 +152,12 @@ Neither `paperlint lint` nor any of the three hooks loads `@ast-grep` or `typesc
|
|
|
166
152
|
|
|
167
153
|
## Troubleshooting
|
|
168
154
|
|
|
169
|
-
| symptom | cause and fix
|
|
170
|
-
| ------------------------------------------------------------ |
|
|
171
|
-
| `init`: skills "nothing linked", or hooks that fail to start | the package is not installed in this project (`npx` ran a temporary copy). Run `npm i -D paperlint`, then `npx paperlint init` again
|
|
172
|
-
| `
|
|
173
|
-
|
|
|
174
|
-
|
|
|
175
|
-
|
|
|
176
|
-
| every
|
|
177
|
-
|
|
|
178
|
-
| anything else | `npx paperlint doctor` — it checks the setup and exits non-zero on anything miswired |
|
|
155
|
+
| symptom | cause and fix |
|
|
156
|
+
| ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
157
|
+
| `init`: skills "nothing linked", or hooks that fail to start | the package is not installed in this project (`npx` ran a temporary copy). Run `npm i -D paperlint`, then `npx paperlint init` again |
|
|
158
|
+
| `paperlint lint`: `no papers in papers/` | there is no paper where `papersDir` points. Create one with `npx paperlint new <name>`, or set `"papersDir"` in `paperlint.json` |
|
|
159
|
+
| hooks fail with `Cannot find module` in a fresh clone | the hook commands run files in `node_modules`: run `npm install` |
|
|
160
|
+
| `paperlint: not built` | installed from git with `--ignore-scripts`, or a clone before building: run `npm run build` in the package |
|
|
161
|
+
| a skill does not show up in Claude Code | its name was already taken in `.claude/skills/`. `init` names it and leaves it alone; rename or remove yours and run `npx paperlint init` again |
|
|
162
|
+
| **every** shell command is blocked in Claude Code | `paper-edit-guard` cannot parse the root `paperlint.json` (merge-conflict markers, a typo), and refuses rather than guess. Fix it with a normal file edit — file edits are not blocked |
|
|
163
|
+
| anything else | `npx paperlint doctor` — it checks the setup and exits non-zero on anything miswired |
|
package/docs/optional-rules.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# Optional rules
|
|
2
2
|
|
|
3
|
-
Some checks matter only for some venues. paperlint ships them **off
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Some checks matter only for some venues. paperlint ships them **off**. A venue preset that needs one
|
|
4
|
+
turns it on for its papers (AgenticDev's turns on `pdf/last-page-balance`), and a paper can turn one
|
|
5
|
+
on or off for itself in its own `paperlint.json`
|
|
6
|
+
([`configuration.md`](configuration.md#a-papers-paperlintjson)). No glob is involved: a paper's
|
|
7
|
+
rules apply to that paper. The root `paperlint.json` can turn it on for every paper, or for some
|
|
8
|
+
with a block ([`configuration.md`](configuration.md#the-rules-key-turning-rules-on-and-off)); a
|
|
9
|
+
paper's own file still has the last word for that paper.
|
|
6
10
|
|
|
7
11
|
| rule | what it checks | who needs it |
|
|
8
12
|
| ----------------------- | ------------------------------------------------------------- | --------------------------------------------------------- |
|
|
@@ -12,21 +16,18 @@ papers that need them, in the `rules` setting of your `package.json`
|
|
|
12
16
|
|
|
13
17
|
### Turning it on
|
|
14
18
|
|
|
19
|
+
A paper that extends `paperlint:agenticdev` has it already, at 120 pt. For any other paper, in its
|
|
20
|
+
`papers/my-paper/paperlint.json`:
|
|
21
|
+
|
|
15
22
|
```json
|
|
16
23
|
{
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
{
|
|
21
|
-
"files": ["papers/agenticdev-2026/**"],
|
|
22
|
-
"rules": { "pdf/last-page-balance": ["error", { "tolerancePt": 120 }] }
|
|
23
|
-
}
|
|
24
|
-
]
|
|
25
|
-
}
|
|
24
|
+
"extends": "paperlint:aisec",
|
|
25
|
+
"kind": "research",
|
|
26
|
+
"rules": { "pdf/last-page-balance": ["error", { "tolerancePt": 120 }] }
|
|
26
27
|
}
|
|
27
28
|
```
|
|
28
29
|
|
|
29
|
-
`
|
|
30
|
+
It applies to that paper alone; `"off"` there turns it off for a paper whose preset turns it on. `tolerancePt` is how far apart, in points, the two
|
|
30
31
|
columns may end; it defaults to 120. On a real accepted paper the balanced build ended 2.7 pt apart
|
|
31
32
|
and the one the publisher sent back 321.4 pt apart — nothing in between — so the default leaves a
|
|
32
33
|
wide margin on both sides.
|
|
@@ -38,17 +39,18 @@ the result to `<paper>/_build/paper.facts.json` ([`configuration.md`](configurat
|
|
|
38
39
|
and the rule judges that file, reporting on the paper's `paper.tex` at the `\documentclass` line.
|
|
39
40
|
So: **build, then lint.**
|
|
40
41
|
|
|
41
|
-
| the rule finds | it says
|
|
42
|
-
| --------------------------------------------------- |
|
|
43
|
-
| columns further apart than `tolerancePt` | **the finding**, with both heights and how to fix it (below)
|
|
44
|
-
| no `_build/paper.facts.json` | build the paper first
|
|
45
|
-
| facts about a different PDF than the one on disk | the facts are stale (their SHA-256 differs) — rebuild
|
|
46
|
-
| facts whose PDF is gone (a failed build removes it) | rebuild
|
|
47
|
-
| a last page of a few lines | nothing — there is no layout to balance
|
|
48
|
-
| a review build with numbered lines | nothing — the numbers run down the whole page, so both columns measure full height, and balance is a camera-ready requirement
|
|
49
|
-
|
|
50
|
-
If you turn the rule on
|
|
51
|
-
so, rather than reporting a clean
|
|
42
|
+
| the rule finds | it says |
|
|
43
|
+
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
44
|
+
| columns further apart than `tolerancePt` | **the finding**, with both heights and how to fix it (below) |
|
|
45
|
+
| no `_build/paper.facts.json` | build the paper first — unless the paper extends a venue preset: then `pdf/measured` already warns, once, and this rule is silent |
|
|
46
|
+
| facts about a different PDF than the one on disk | the facts are stale (their SHA-256 differs) — rebuild |
|
|
47
|
+
| facts whose PDF is gone (a failed build removes it) | rebuild |
|
|
48
|
+
| a last page of a few lines | nothing — there is no layout to balance |
|
|
49
|
+
| a review build with numbered lines | nothing — the numbers run down the whole page, so both columns measure full height, and balance is a camera-ready requirement |
|
|
50
|
+
|
|
51
|
+
If you turn the rule on — in a `paperlint.json` beside no `paper.tex`, or with a `files` glob in
|
|
52
|
+
the root `paperlint.json` that reaches none — `paperlint lint` fails and says so, rather than reporting a clean
|
|
53
|
+
run for a rule that never ran.
|
|
52
54
|
|
|
53
55
|
### Which venues need it
|
|
54
56
|
|