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.
- package/CLAUDE.md +6 -1
- package/README.md +233 -224
- 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.d.ts +15 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +38 -4
- package/dist/build.js.map +1 -1
- package/dist/cli.d.ts +28 -25
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +277 -265
- package/dist/cli.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +40 -72
- package/dist/doctor.js.map +1 -1
- package/dist/facts-file.d.ts +2 -2
- package/dist/facts-file.js +2 -2
- 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 +39 -29
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +143 -172
- 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.map +1 -1
- package/dist/new-paper.js +2 -13
- 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 +13 -41
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +71 -71
- package/dist/paper-settings.js.map +1 -1
- 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 +1 -1
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +4 -6
- package/dist/presets.js.map +1 -1
- 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 +5 -3
- package/dist/rules-config.d.ts.map +1 -1
- package/dist/rules-config.js +15 -6
- package/dist/rules-config.js.map +1 -1
- package/dist/structure.js +3 -3
- package/dist/structure.js.map +1 -1
- package/dist/tex-requirements.js +2 -2
- 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.map +1 -1
- package/dist/venue-rules.js +11 -9
- package/dist/venue-rules.js.map +1 -1
- package/docs/configuration.md +119 -104
- package/docs/e2e.md +1 -1
- package/docs/install.md +25 -40
- package/docs/optional-rules.md +5 -4
- package/docs/rules.md +60 -23
- 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 -31
- package/eslint-rules/pdf-last-page-balance.mjs +4 -7
- 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/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 -25
- package/lib/paper-config.harness.mjs +18 -35
- package/lib/paper-config.mjs +83 -105
- 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/grade-paper-writing/SKILL.md +20 -1
- package/skills/grade-paper-writing/SKILL.md.spec.ts +19 -0
- 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/render-paper.harness.mjs +2 -2
- 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.harness.mjs +8 -4
- package/src/build.ts +62 -6
- package/src/cli.harness.mjs +147 -292
- package/src/cli.mutations.mjs +35 -44
- package/src/cli.ts +344 -304
- package/src/doctor.harness.mjs +41 -84
- package/src/doctor.mutations.mjs +9 -9
- package/src/doctor.ts +44 -89
- package/src/facts-file.test.ts +0 -7
- package/src/facts-file.ts +2 -2
- 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 +186 -230
- 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.test.ts +1 -13
- package/src/new-paper.ts +2 -17
- package/src/paper-files.test.ts +290 -0
- package/src/paper-files.ts +144 -0
- package/src/paper-settings-commands.test.ts +84 -121
- package/src/paper-settings.test.ts +93 -64
- package/src/paper-settings.ts +99 -97
- package/src/ports/check-references.ts +28 -0
- package/src/presets.ts +5 -15
- 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 +19 -7
- package/src/structure.harness.mjs +3 -1
- package/src/structure.ts +3 -3
- package/src/tex-requirements.ts +2 -2
- package/src/types.ts +30 -18
- package/src/venue-rules.test.ts +20 -11
- package/src/venue-rules.ts +11 -13
- 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/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,85 +1,74 @@
|
|
|
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/old-draft/**"],
|
|
22
|
-
"rules": { "paper/typography": "off" }
|
|
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
|
-
`papersDir` is required because the scope is the one thing that must not default: a default of `"."`
|
|
50
|
-
turns every run into a green report over the whole checkout. `paperlint init` fills it by measuring —
|
|
51
|
-
and when nothing on disk looks like a papers directory, it writes the documented default and says
|
|
52
|
-
in the same breath that it is a guess.
|
|
53
|
-
|
|
54
|
-
Until 2026-09-24 this field was called `papers`. The old name is not read as a fallback: `paperlint lint`,
|
|
55
|
-
`paperlint init`, `paperlint doctor`, the ESLint helper and the edit guard all stop with
|
|
56
|
-
`"papers" was renamed to "papersDir" in package.json → "paperlint"`. The two advisory
|
|
57
|
-
hooks stay silent instead. The name is defined once, as `PAPERS_DIR_FIELD` in
|
|
58
|
-
`lib/paper-config.mjs`.
|
|
59
|
-
|
|
60
|
-
## Three levels of settings
|
|
61
|
-
|
|
62
|
-
Each level is named after the tool, and each says something the others cannot:
|
|
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:
|
|
63
56
|
|
|
64
57
|
```
|
|
65
|
-
|
|
66
|
-
papers/
|
|
67
|
-
my-paper/
|
|
68
|
-
paper.tex
|
|
69
|
-
PIPELINE-STATUS.md
|
|
70
|
-
paperlint.json { "extends": … } THIS PAPER: its venue preset, its kind, its own rules — `paperlint new` writes it
|
|
71
|
-
venues/usenix-sec.jsonc (optional, your own) a VENUE PRESET: format, page limits, TeX packages, rules
|
|
72
|
-
node_modules/paperlint/skills/submit-paper/references/venues/
|
|
73
|
-
acm-sigconf.jsonc agenticdev.jsonc aisec.jsonc realm.jsonc the shipped presets (paperlint:<name>)
|
|
58
|
+
no papers in papers/ — create one with `npx paperlint new <name>`, or set "papersDir" in paperlint.json if your papers live elsewhere
|
|
74
59
|
```
|
|
75
60
|
|
|
76
|
-
`paperlint
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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`
|
|
83
72
|
|
|
84
73
|
```json
|
|
85
74
|
{
|
|
@@ -89,51 +78,70 @@ changes nothing else.
|
|
|
89
78
|
}
|
|
90
79
|
```
|
|
91
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
|
+
|
|
92
85
|
| key | what it is |
|
|
93
86
|
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
94
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) |
|
|
95
88
|
| `kind` | the kind of paper (`short`, `research`, …) whose page limit applies |
|
|
96
89
|
| `pdf` | where the built PDF is, relative to the paper, when it is not `paper.pdf` |
|
|
97
|
-
| `rules` | rule
|
|
98
|
-
| `$comment` | a note for humans
|
|
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".
|
|
99
98
|
|
|
100
|
-
Any other key is an error
|
|
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`.
|
|
101
102
|
|
|
102
103
|
**Where a paper's rules come from, in order — a later one wins, rule by rule:** paperlint's own
|
|
103
104
|
configuration → the preset chain's `rules`, from the root preset to the one the paper extends →
|
|
104
|
-
the
|
|
105
|
-
|
|
106
|
-
|
|
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.
|
|
107
108
|
|
|
108
|
-
|
|
109
|
-
read: a paper with only a `venue.json` gets a `pdf/profile` error and `paperlint doctor` names the
|
|
110
|
-
file, both pointing at `init`. `init` writes the same settings as `paperlint.json` —
|
|
111
|
-
`"venue": "aisec"` becomes `"extends": "paperlint:aisec"`, and the `"_"` some files used as a
|
|
112
|
-
comment becomes `"$comment"` — then deletes `venue.json`. It removes a `venue.json` whose
|
|
113
|
-
`paperlint.json` already says the same, and refuses, changing nothing, when both exist and differ.
|
|
109
|
+
## Records in frontmatter, checked by shipped JSON Schemas
|
|
114
110
|
|
|
115
|
-
|
|
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`):
|
|
116
113
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
import code and cannot walk up a tree looking for a config; it can read a path it is able to name,
|
|
120
|
-
and the one path it can always name is the project's `package.json`.
|
|
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:
|
|
121
116
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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.
|
|
125
131
|
|
|
126
132
|
## The `rules` key: turning rules on and off
|
|
127
133
|
|
|
128
|
-
`rules`
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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.
|
|
133
142
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
override what a paper says:
|
|
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.
|
|
137
145
|
|
|
138
146
|
```json
|
|
139
147
|
"rules": [
|
|
@@ -143,20 +151,25 @@ override what a paper says:
|
|
|
143
151
|
},
|
|
144
152
|
{
|
|
145
153
|
"files": ["papers/old-draft/**"],
|
|
146
|
-
"rules": { "paper/
|
|
154
|
+
"rules": { "paper/section-word": "off" }
|
|
147
155
|
}
|
|
148
156
|
]
|
|
149
157
|
```
|
|
150
158
|
|
|
151
|
-
- **`files` and `ignores` are globs relative to the file that holds
|
|
152
|
-
|
|
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,
|
|
153
161
|
whatever directory you run `paperlint lint` from. A block without `files` applies to every linted file.
|
|
154
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.
|
|
155
168
|
- **A rule entry** is a severity (`"off"`, `"warn"`, `"error"`, or `0`/`1`/`2`), or a list whose
|
|
156
169
|
first element is a severity and the rest are the rule's options.
|
|
157
170
|
- **Only rules paperlint ships can be named** — the ones in [`docs/rules.md`](rules.md) and
|
|
158
171
|
[`docs/optional-rules.md`](optional-rules.md). A rule id paperlint does not ship, a bad severity, a
|
|
159
|
-
`rules`
|
|
172
|
+
`rules` of another shape, or a block key other than `files`, `ignores` and `rules` stops the run
|
|
160
173
|
with a message naming the exact key, before anything is linted.
|
|
161
174
|
- **An optional rule you turned on must reach a paper.** If no linted `paper.tex` gets the rule —
|
|
162
175
|
usually a `files` glob with a typo — `paperlint lint` fails and says so: a rule that never runs
|
|
@@ -271,7 +284,7 @@ having run on it, because a missing build read as nothing to do.
|
|
|
271
284
|
|
|
272
285
|
⚠️ **A `build.sh` or `repro/build-submission.sh` in the paper directory is IGNORED.** Earlier
|
|
273
286
|
versions ran it; `paperlint build` now says one line — `build.sh is ignored — paperlint builds the paper
|
|
274
|
-
itself` — and builds the paper itself.
|
|
287
|
+
itself` — and builds the paper itself.
|
|
275
288
|
Why: [#59](https://github.com/zernie/paperlint/issues/59).
|
|
276
289
|
|
|
277
290
|
## Using the rules from an existing ESLint config
|
|
@@ -285,7 +298,9 @@ the exact config the CLI uses, so the shortest path is:
|
|
|
285
298
|
// eslint.config.mjs
|
|
286
299
|
import { buildConfig } from "paperlint/bin/paperlint.mjs";
|
|
287
300
|
import { texLanguage } from "paperlint/eslint-rules/latex-language.mjs";
|
|
288
|
-
export default buildConfig({
|
|
301
|
+
export default buildConfig({}, texLanguage);
|
|
289
302
|
```
|
|
290
303
|
|
|
291
|
-
|
|
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
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
Some checks matter only for some venues. paperlint ships them **off**. A venue preset that needs one
|
|
4
4
|
turns it on for its papers (AgenticDev's turns on `pdf/last-page-balance`), and a paper can turn one
|
|
5
5
|
on or off for itself in its own `paperlint.json`
|
|
6
|
-
([`configuration.md`](configuration.md#
|
|
7
|
-
rules apply to that paper. The `
|
|
8
|
-
|
|
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.
|
|
9
10
|
|
|
10
11
|
| rule | what it checks | who needs it |
|
|
11
12
|
| ----------------------- | ------------------------------------------------------------- | --------------------------------------------------------- |
|
|
@@ -48,7 +49,7 @@ So: **build, then lint.**
|
|
|
48
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 |
|
|
49
50
|
|
|
50
51
|
If you turn the rule on — in a `paperlint.json` beside no `paper.tex`, or with a `files` glob in
|
|
51
|
-
`
|
|
52
|
+
the root `paperlint.json` that reaches none — `paperlint lint` fails and says so, rather than reporting a clean
|
|
52
53
|
run for a rule that never ran.
|
|
53
54
|
|
|
54
55
|
### Which venues need it
|