paperlint 9.0.1 → 10.1.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/CONTRIBUTING.md +16 -4
- package/README.md +68 -128
- package/dist/build.d.ts +1 -1
- package/dist/build.js +1 -1
- package/dist/cli.d.ts +5 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +52 -6
- package/dist/cli.js.map +1 -1
- package/dist/domain/layout-tree.d.ts +19 -0
- package/dist/domain/layout-tree.d.ts.map +1 -0
- package/dist/domain/layout-tree.js +32 -0
- package/dist/domain/layout-tree.js.map +1 -0
- package/dist/domain/lookup-cache.d.ts +2 -2
- package/dist/domain/lookup-cache.js +2 -2
- package/dist/domain/paper-layout.d.ts +81 -0
- package/dist/domain/paper-layout.d.ts.map +1 -0
- package/dist/domain/paper-layout.js +259 -0
- package/dist/domain/paper-layout.js.map +1 -0
- package/dist/domain/repro-layout.d.ts +80 -0
- package/dist/domain/repro-layout.d.ts.map +1 -0
- package/dist/domain/repro-layout.js +247 -0
- package/dist/domain/repro-layout.js.map +1 -0
- package/dist/domain/scaffold-layout.d.ts +28 -0
- package/dist/domain/scaffold-layout.d.ts.map +1 -0
- package/dist/domain/scaffold-layout.js +81 -0
- package/dist/domain/scaffold-layout.js.map +1 -0
- package/dist/eslint-rules/js-needs-reason.d.ts +55 -0
- package/dist/eslint-rules/js-needs-reason.d.ts.map +1 -0
- package/dist/eslint-rules/js-needs-reason.js +35 -0
- package/dist/eslint-rules/js-needs-reason.js.map +1 -0
- package/dist/eslint-rules/prereg-coverage.d.ts +98 -0
- package/dist/eslint-rules/prereg-coverage.d.ts.map +1 -0
- package/dist/eslint-rules/prereg-coverage.js +274 -0
- package/dist/eslint-rules/prereg-coverage.js.map +1 -0
- package/dist/folder-layout-hook.d.ts +21 -0
- package/dist/folder-layout-hook.d.ts.map +1 -0
- package/dist/folder-layout-hook.js +117 -0
- package/dist/folder-layout-hook.js.map +1 -0
- package/dist/folder-layout-rule.d.ts +22 -0
- package/dist/folder-layout-rule.d.ts.map +1 -0
- package/dist/folder-layout-rule.js +107 -0
- package/dist/folder-layout-rule.js.map +1 -0
- package/dist/init.js +1 -1
- package/dist/init.js.map +1 -1
- package/dist/new-paper.d.ts +4 -1
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +40 -6
- package/dist/new-paper.js.map +1 -1
- package/dist/package-dirs.d.ts +6 -0
- package/dist/package-dirs.d.ts.map +1 -1
- package/dist/package-dirs.js +6 -0
- package/dist/package-dirs.js.map +1 -1
- package/dist/references.d.ts +1 -1
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +2 -2
- package/dist/references.js.map +1 -1
- package/dist/repro-layout-rule.d.ts +16 -0
- package/dist/repro-layout-rule.d.ts.map +1 -0
- package/dist/repro-layout-rule.js +81 -0
- package/dist/repro-layout-rule.js.map +1 -0
- package/dist/skills/label-agent-runs/scripts/labels-commands.d.ts +13 -0
- package/dist/skills/label-agent-runs/scripts/labels.d.ts +2 -0
- package/dist/skills/run-agent-grid/scripts/grid-guard-commands.d.ts +20 -0
- package/dist/skills/run-agent-grid/scripts/grid-guard.d.ts +2 -0
- package/docs/ci.md +60 -0
- package/docs/configuration.md +2 -0
- package/docs/e2e.md +2 -2
- package/docs/install.md +20 -16
- package/docs/layout.md +154 -0
- package/docs/optional-rules.md +5 -4
- package/docs/references.md +5 -2
- package/docs/rules/paper/allowed-files-only.md +192 -0
- package/docs/rules/repro/allowed-files-only.md +128 -0
- package/docs/rules/study/declare-sample.md +146 -0
- package/docs/rules.md +53 -50
- package/docs/skills.md +8 -3
- package/docs/testing.md +13 -0
- package/docs/venues.md +94 -0
- package/eslint-rules/js-needs-reason.test.ts +141 -0
- package/eslint-rules/js-needs-reason.ts +77 -0
- package/eslint-rules/prereg-coverage.test.ts +448 -0
- package/eslint-rules/prereg-coverage.ts +419 -0
- package/eslint.config.mjs +22 -2
- package/lib/skill-trigger-cases.harness.mjs +51 -5
- package/lib/skill-trigger-cases.mjs +53 -15
- package/lib/skill-trigger-cases.test.ts +211 -0
- package/package.json +8 -4
- package/plugin/hooks/hooks.json +9 -0
- package/scripts/coverage-config.test.ts +5 -3
- package/skills/analyze-sibling-paper/SKILL.md +2 -2
- package/skills/analyze-sibling-paper/SKILL.md.spec.ts +1 -1
- package/skills/build-benchmark/SKILL.md +17 -1
- package/skills/build-benchmark/SKILL.md.spec.ts +16 -0
- package/skills/build-label-page/SKILL.md +120 -0
- package/skills/build-label-page/SKILL.md.spec.ts +131 -0
- package/skills/build-label-page/build-label-page.eval.mjs +15 -0
- package/skills/build-label-page/build-label-page.test.ts +959 -0
- package/skills/build-label-page/references/checklist.md +148 -0
- package/skills/build-label-page/references/example-generator.py +803 -0
- package/skills/find-venue/SKILL.md +5 -3
- package/skills/find-venue/SKILL.md.spec.ts +4 -2
- package/skills/grade-paper-writing/SKILL.md +7 -7
- package/skills/grade-paper-writing/SKILL.md.spec.ts +6 -6
- package/skills/label-agent-runs/SKILL.md +103 -0
- package/skills/label-agent-runs/SKILL.md.spec.ts +106 -0
- package/skills/label-agent-runs/label-agent-runs.eval.ts +55 -0
- package/skills/label-agent-runs/label-agent-runs.harness.ts +123 -0
- package/skills/label-agent-runs/references/formats.md +64 -0
- package/skills/label-agent-runs/scripts/labels-commands.js +13 -0
- package/skills/label-agent-runs/scripts/labels-commands.ts +13 -0
- package/skills/label-agent-runs/scripts/labels.js +396 -0
- package/skills/label-agent-runs/scripts/labels.test.ts +538 -0
- package/skills/label-agent-runs/scripts/labels.ts +501 -0
- package/skills/map-prior-work/SKILL.md +2 -2
- package/skills/map-prior-work/SKILL.md.spec.ts +1 -1
- package/skills/paper-pipeline/SKILL.md +15 -6
- package/skills/paper-pipeline/SKILL.md.spec.ts +14 -5
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +19 -9
- package/skills/paper-pipeline/references/acceptance-gate.md +19 -19
- package/skills/paper-pipeline/references/artifact-checklist.md +5 -0
- package/skills/paper-pipeline/references/pipeline-status-template.md +30 -1
- package/skills/paper-pipeline/repro/2026-08-07-fork-check.log +1 -1
- package/skills/paper-pipeline/repro/2026-08-07-fork-check2.log +1 -1
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-pilot.log +1 -1
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-raw.log +1 -1
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.log +48 -48
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-oracle.log +8 -8
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.log +48 -48
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.log +2 -2
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication.mjs +3 -3
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-ablation-preflight-after.log +81 -0
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-ablation-preflight-before.log +81 -0
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-ablation-setupdiff.log +59 -0
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-trigger.jsonl +1 -0
- package/skills/paper-pipeline/repro/2026-10-13-grade-paper-writing-trigger.log +22 -0
- package/skills/paper-pipeline/repro/README.md +4 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +2 -2
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +2 -2
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +4 -2
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +7 -2
- package/skills/paper-pipeline/scripts/generated-code.mjs +8 -4
- package/skills/paper-pipeline/scripts/generated-code.test.ts +15 -1
- package/skills/paper-status/SKILL.md +2 -2
- package/skills/paper-status/SKILL.md.spec.ts +1 -1
- package/skills/run-agent-grid/SKILL.md +181 -0
- package/skills/run-agent-grid/SKILL.md.spec.ts +184 -0
- package/skills/run-agent-grid/references/failure-catalog.md +36 -0
- package/skills/run-agent-grid/references/registry-and-ledger.md +142 -0
- package/skills/run-agent-grid/run-agent-grid.eval.ts +59 -0
- package/skills/run-agent-grid/run-agent-grid.harness.ts +115 -0
- package/skills/run-agent-grid/scripts/grid-guard-commands.js +18 -0
- package/skills/run-agent-grid/scripts/grid-guard-commands.ts +21 -0
- package/skills/run-agent-grid/scripts/grid-guard.js +700 -0
- package/skills/run-agent-grid/scripts/grid-guard.test.ts +1090 -0
- package/skills/run-agent-grid/scripts/grid-guard.ts +869 -0
- package/skills/study-accepted-papers/SKILL.md +163 -161
- package/skills/study-accepted-papers/SKILL.md.spec.ts +167 -165
- package/skills/study-accepted-papers/references/base-rates.md +69 -0
- package/skills/study-accepted-papers/references/mechanisms.md +181 -0
- package/skills/verify-citations/SKILL.md +2 -2
- package/skills/verify-citations/SKILL.md.spec.ts +1 -1
- package/src/build.ts +1 -1
- package/src/cli.harness.mjs +42 -0
- package/src/cli.test.ts +90 -6
- package/src/cli.ts +78 -5
- package/src/domain/layout-tree.test.ts +32 -0
- package/src/domain/layout-tree.ts +47 -0
- package/src/domain/lookup-cache.ts +2 -2
- package/src/domain/paper-layout.test.ts +264 -0
- package/src/domain/paper-layout.ts +336 -0
- package/src/domain/repro-layout.test.ts +222 -0
- package/src/domain/repro-layout.ts +311 -0
- package/src/domain/scaffold-layout.ts +104 -0
- package/src/folder-layout-hook.test.ts +214 -0
- package/src/folder-layout-hook.ts +141 -0
- package/src/folder-layout-rule.test.ts +235 -0
- package/src/folder-layout-rule.ts +151 -0
- package/src/hooks-settings.harness.ts +9 -8
- package/src/init.ts +1 -1
- package/src/layout-docs.test.ts +52 -0
- package/src/new-paper.harness.mjs +23 -1
- package/src/new-paper.test.ts +299 -6
- package/src/new-paper.ts +51 -5
- package/src/package-dirs.ts +8 -0
- package/src/paper-files.test.ts +9 -6
- package/src/references.test.ts +5 -5
- package/src/references.ts +2 -2
- package/src/repro-layout-rule.test.ts +114 -0
- package/src/repro-layout-rule.ts +121 -0
- package/src/rule-cases.test.ts +98 -0
- package/templates/paper/PIPELINE-STATUS.md +41 -7
- package/tsconfig.base.json +22 -0
- package/tsconfig.json +6 -24
- package/tsconfig.pkg.json +3 -1
- package/tsconfig.skills.json +14 -0
- package/tsconfig.src.json +12 -0
package/CONTRIBUTING.md
CHANGED
|
@@ -132,13 +132,25 @@ site without `npx eslint . --prune-suppressions` fails lint, and #134 burns the
|
|
|
132
132
|
New code is TypeScript (#78). The last block of [`eslint.config.mjs`](eslint.config.mjs) makes any
|
|
133
133
|
`.js`, `.mjs` or `.cjs` file an error in the directories already converted (`TYPESCRIPT_ONLY`), so
|
|
134
134
|
`npm run lint` fails on one; a directory that still mixes the two is not listed yet, and the change
|
|
135
|
-
that converts it adds it.
|
|
135
|
+
that converts it adds it. Everywhere else, a NEW `.js`, `.mjs` or `.cjs` file fails `local/js-needs-reason`
|
|
136
|
+
unless its top comment says `JavaScript, not TypeScript: evidence|fixture|config-loader — <why>`;
|
|
137
|
+
the files that predate the rule are bulk suppressions in `eslint-suppressions.json`, which only
|
|
138
|
+
shrinks. Code that only this repository runs (tests, `scripts/`, `test/e2e/`) is
|
|
136
139
|
run by `node` directly; code a consumer runs from `node_modules` needs a build first, because Node
|
|
137
140
|
does not strip types there.
|
|
138
141
|
|
|
139
|
-
`npm run build` is
|
|
140
|
-
`
|
|
141
|
-
`
|
|
142
|
+
`npm run build` is `tsc -b`: TypeScript's build mode over the project references in the root
|
|
143
|
+
`tsconfig.json`, which is a solution file and compiles nothing itself. The three projects share
|
|
144
|
+
`tsconfig.base.json`: `tsconfig.pkg.json` builds `lib/` and `eslint-rules/` to `dist/lib/` and
|
|
145
|
+
`dist/eslint-rules/`; `tsconfig.src.json` builds `src/` to `dist/*.js` and references the first,
|
|
146
|
+
whose declarations it reads; `tsconfig.skills.json` compiles a skill's scripts
|
|
147
|
+
(`skills/*/scripts/*.ts`) to a `.js` BESIDE each, gitignored and shipped through `files` (their
|
|
148
|
+
declarations go to `dist/skills/`): a skill names its script as `${CLAUDE_SKILL_DIR}/scripts/<name>.js`,
|
|
149
|
+
and only a path inside the skill's own directory resolves the same through the consumer's
|
|
150
|
+
`.claude/skills/<name>` link and in this checkout. A second `tsc -b` with nothing changed is a no-op;
|
|
151
|
+
it decides that from `dist/.tsbuildinfo/`, so an output deleted by hand is rebuilt only by
|
|
152
|
+
`npx tsc -b --force` (or by deleting `dist/` with it). `tsconfig.test.json` is not part of the build:
|
|
153
|
+
it type-checks every `.ts` without emitting. Everything else imports `lib/` and `eslint-rules/` through the package's subpath imports,
|
|
142
154
|
`#lib/<name>` and `#eslint-rules/<name>` (`"imports"` in `package.json`): the default target is the
|
|
143
155
|
build, which is what an install runs; the `paperlint-source` condition points at the `.ts` instead,
|
|
144
156
|
and `npm test` (so `npm run coverage` and `npm run check` too) and `tsconfig.test.json` set it so
|
package/README.md
CHANGED
|
@@ -6,6 +6,7 @@ README style — keep it scannable, not a wall of text:
|
|
|
6
6
|
- Tables for comparisons (commands, venues, checks). Code blocks for every command and file.
|
|
7
7
|
- A blank line between every block. Bold only for the one phrase a skimmer must see.
|
|
8
8
|
- Mechanics and edge cases live in docs/, linked — not inline.
|
|
9
|
+
- Budgets, held by test/readme-shape.test.ts: ≤ 300 lines, no ## section over 60, the install command in the first 90 lines. Over budget → move material to docs/ and link it; raising a budget is a decision, not a fix.
|
|
9
10
|
- A reader must be able to answer "what is it, which venues, how do I start" from the first screen.
|
|
10
11
|
- 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
|
- One name per thing, everywhere (lint, build, venue preset, kind, skill, papersDir), each explained once, before it is used.
|
|
@@ -37,37 +38,15 @@ before you submit.** It comes in two parts:
|
|
|
37
38
|
|
|
38
39
|
## Contents
|
|
39
40
|
|
|
40
|
-
- [The pipeline](#-the-pipeline)
|
|
41
41
|
- [Supported venues](#-supported-venues)
|
|
42
42
|
- [Getting started](#-getting-started)
|
|
43
|
-
|
|
44
|
-
- [A paper you already have](#a-paper-you-already-have)
|
|
43
|
+
- [The pipeline](#-the-pipeline)
|
|
45
44
|
- [Commands](#-commands)
|
|
46
45
|
- [Skills](#-skills)
|
|
47
46
|
- [Lint and build](#-lint-and-build)
|
|
48
|
-
- [Configuration](#-configuration)
|
|
49
|
-
- [Add a venue that isn't listed](#-add-a-venue-that-isnt-listed)
|
|
47
|
+
- [Configuration](#-configuration) · [add a venue](#-add-a-venue-that-isnt-listed)
|
|
50
48
|
- [Run it in CI](#-run-it-in-ci)
|
|
51
|
-
- [FAQ](#-faq)
|
|
52
|
-
- [Docs](#-docs)
|
|
53
|
-
|
|
54
|
-
## 🧭 The pipeline
|
|
55
|
-
|
|
56
|
-
```text
|
|
57
|
-
stage what you get skills linter
|
|
58
|
-
1 idea a go / no-go, with the reason yes
|
|
59
|
-
2 venue venues ranked, deadlines planned yes
|
|
60
|
-
3 study a study design, honest statistics yes
|
|
61
|
-
4 draft a first draft, then a tighter one yes lint
|
|
62
|
-
5 review the objections, before the reviewers yes lint
|
|
63
|
-
6 submit a PDF within the limit, recorded yes build, lint
|
|
64
|
-
7 camera-ready the final, de-anonymised PDF yes build, lint
|
|
65
|
-
8 extend a plan for the next paper yes
|
|
66
|
-
|
|
67
|
-
skills: optional, with Claude Code
|
|
68
|
-
lint: every edit and every CI run, once the paper exists (paperlint new)
|
|
69
|
-
build: compiles and measures the PDF; lint then checks pages, fonts, references
|
|
70
|
-
```
|
|
49
|
+
- [FAQ](#-faq) · [Docs](#-docs)
|
|
71
50
|
|
|
72
51
|
## 🎯 Supported venues
|
|
73
52
|
|
|
@@ -94,24 +73,18 @@ REALM.
|
|
|
94
73
|
|
|
95
74
|
<!-- `vigiles:symbol src/init.ts#init` — `npm run check` fails if this function is renamed or removed. -->
|
|
96
75
|
|
|
97
|
-
1. Install (Node 22.13 or newer)
|
|
76
|
+
1. Install (Node 22.13 or newer) and set up. `init` asks where your papers live (default
|
|
77
|
+
`papers/`), whether to install TeX Live now, and whether to add a CI workflow; with Claude Code
|
|
78
|
+
it also installs the skills and four hooks ([what they do](docs/install.md)).
|
|
98
79
|
|
|
99
80
|
```sh
|
|
100
81
|
npm i -D paperlint
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
2. Set up. `init` asks three questions: where your papers live (default `papers/`), whether to
|
|
104
|
-
install TeX Live now, and whether to add a CI workflow. With Claude Code it also installs the
|
|
105
|
-
skills and three hooks ([what they do](docs/install.md)).
|
|
106
|
-
|
|
107
|
-
```sh
|
|
108
82
|
npx paperlint init
|
|
109
83
|
```
|
|
110
84
|
|
|
111
|
-
|
|
112
|
-
limits applies. Name the folder after the work, not the venue: a rejected paper moves
|
|
113
|
-
|
|
114
|
-
the word really is the work's):
|
|
85
|
+
2. Create a paper. `--venue` names its venue preset from the table above, `--kind` which of its page
|
|
86
|
+
limits applies. Name the folder after the work, not the venue: a rejected paper moves on and keeps
|
|
87
|
+
its folder, so `new` refuses a name like `aisec-2026`.
|
|
115
88
|
|
|
116
89
|
<!-- `vigiles:symbol src/new-paper.ts#newPaper` — `npm run check` fails if this function is renamed or removed. -->
|
|
117
90
|
<!-- `vigiles:symbol src/new-paper.ts#ALLOW_VENUE_NAME` — the flag's one spelling; `npm run check` fails if it is renamed or removed. -->
|
|
@@ -120,25 +93,19 @@ REALM.
|
|
|
120
93
|
npx paperlint new my-paper --venue agenticdev --kind short
|
|
121
94
|
```
|
|
122
95
|
|
|
123
|
-
It writes `papers/my-paper/`
|
|
124
|
-
-
|
|
125
|
-
-
|
|
126
|
-
- `PIPELINE-STATUS.md` — which stage the paper is at; the skills read and update it.
|
|
96
|
+
It writes `papers/my-paper/` with `paper.tex`, `paperlint.json` (venue and kind),
|
|
97
|
+
`PIPELINE-STATUS.md` (the stage the paper is at) and the empty folders of the
|
|
98
|
+
[layout](docs/layout.md#the-map).
|
|
127
99
|
|
|
128
|
-
|
|
129
|
-
`init`, run `npx paperlint toolchain` first.
|
|
100
|
+
3. Build the PDF. **This needs TeX Live, a ~270 MB download (~3 min, once)**; if you said no in
|
|
101
|
+
`init`, run `npx paperlint toolchain` first. Then check it:
|
|
130
102
|
|
|
131
103
|
```sh
|
|
132
104
|
npx paperlint build papers/my-paper
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
5. Check it:
|
|
136
|
-
|
|
137
|
-
```sh
|
|
138
105
|
npx paperlint lint
|
|
139
106
|
```
|
|
140
107
|
|
|
141
|
-
On the stub from step
|
|
108
|
+
On the stub from step 2 the first run already catches something (output shortened):
|
|
142
109
|
|
|
143
110
|
```text
|
|
144
111
|
papers/my-paper/paper.tex
|
|
@@ -152,13 +119,34 @@ REALM.
|
|
|
152
119
|
|
|
153
120
|
### A paper you already have
|
|
154
121
|
|
|
155
|
-
|
|
122
|
+
Put it at `papers/my-paper/paper.tex` (or [set `papersDir`](#-configuration) to where your papers
|
|
123
|
+
live), then run the same `new` command: on a folder that holds anything it only adds the missing
|
|
124
|
+
`paperlint.json` and `PIPELINE-STATUS.md`, and never touches `paper.tex`. Build and lint as above.
|
|
125
|
+
|
|
126
|
+
## 🧭 The pipeline
|
|
127
|
+
|
|
128
|
+
A paper goes through eight stages. The skills help at each one; lint checks the folder from the
|
|
129
|
+
moment it exists.
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
stage what you get where it goes
|
|
133
|
+
1 idea a go / no-go, with the reason siblings/
|
|
134
|
+
2 venue venues ranked, deadlines planned paperlint.json, the venue's corpus
|
|
135
|
+
3 study a study design, honest statistics repro/<study>/
|
|
136
|
+
4 draft a first draft, then a tighter one paper.tex, sections/, figures/
|
|
137
|
+
5 review the objections, before the reviewers reviews/, PIPELINE-STATUS.md
|
|
138
|
+
6 submit a PDF within the limit, recorded paper.pdf, versions/, submission/
|
|
139
|
+
7 camera-ready the final, de-anonymised PDF versions/, talk/
|
|
140
|
+
8 extend a plan for the next paper a new paper folder
|
|
141
|
+
|
|
142
|
+
skills: every stage; optional, with Claude Code
|
|
143
|
+
lint: any stage once the paper folder exists (paperlint new) — when you run it, and in CI
|
|
144
|
+
build: stages 6–7; compiles and measures the PDF, and lint then checks pages, fonts, references
|
|
145
|
+
hook: with Claude Code, refuses a new file in the wrong place before it is written
|
|
146
|
+
```
|
|
156
147
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
2. Run `npx paperlint new my-paper --venue <preset> --kind <kind>`. On an existing folder it only
|
|
160
|
-
adds the missing `paperlint.json` and `PIPELINE-STATUS.md`; it never touches `paper.tex`.
|
|
161
|
-
3. Build and lint, as above.
|
|
148
|
+
Each stage's skills, what it writes and what checks it, drawn as one flow, with the folder layout:
|
|
149
|
+
[`docs/layout.md`](docs/layout.md).
|
|
162
150
|
|
|
163
151
|
## 🧰 Commands
|
|
164
152
|
|
|
@@ -182,6 +170,7 @@ what it does — "is this idea worth a paper?", "find me a venue for this". `ini
|
|
|
182
170
|
|
|
183
171
|
- **A go / no-go on your idea, with the reason.** `research-ideate`
|
|
184
172
|
- **A ranked list of venues that fit** — deadline, page limit, indexing. `find-venue`
|
|
173
|
+
- **A page to label the study's items on your phone**, blind, with the answers exported into the paper's folder. `build-label-page`
|
|
185
174
|
- **A first draft from your results.** `draft-paper`
|
|
186
175
|
- **A hostile review before the real one.** `paper-adversarial-review`
|
|
187
176
|
- **A ready / not ready verdict before you submit**, worst problem first. `harden-paper`
|
|
@@ -192,52 +181,34 @@ Every skill, by stage: [`docs/skills.md`](docs/skills.md).
|
|
|
192
181
|
|
|
193
182
|
## 🔍 Lint and build
|
|
194
183
|
|
|
195
|
-
| | `paperlint build`
|
|
196
|
-
| ------ |
|
|
197
|
-
| needs | Node, TeX Live, the network
|
|
198
|
-
| does | compiles `paper.tex`, measures the PDF, checks each reference online (exists, right authors)
|
|
199
|
-
| writes | `paper.pdf`,
|
|
184
|
+
| | `paperlint build` | `paperlint lint` |
|
|
185
|
+
| ------ | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
186
|
+
| needs | Node, TeX Live, the network | Node |
|
|
187
|
+
| does | compiles `paper.tex`, measures the PDF, checks each reference online (exists, right authors) | checks the source, and judges what build recorded: page limit, fonts, references; offline |
|
|
188
|
+
| writes | `paper.pdf`, `_build/`, `repro/data/references-cache.json` (commit it) | nothing (`--fix`: the three fixable slips) |
|
|
200
189
|
|
|
201
190
|
- If `paper.pdf` or the bibliography changed since the last build, lint fails and tells you to rebuild.
|
|
202
|
-
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
- A paper whose body is in other files (`\input{sections/intro}`) is linted file by file: a finding
|
|
206
|
-
lands at the file and line it is in, and `--fix` edits that file. The files are the ones TeX read
|
|
207
|
-
after `\begin{document}` in the last build (`_build/sources.json`) — not every `.tex` beside it,
|
|
208
|
-
and not an included `.bbl` (a tool's output, read only as part of the whole paper). A paper that
|
|
209
|
-
was not built, or changed since, is linted as `paper.tex` alone, and `paper/sources-fresh` says so.
|
|
210
|
-
- Lint also checks the pipeline's own records: `PIPELINE-STATUS.md`, reviews, notes on related
|
|
211
|
-
papers ([`docs/rules.md`](docs/rules.md)).
|
|
212
|
-
- The rules run on ESLint, so a deliberate exception is a comment on the line above:
|
|
191
|
+
- Lint also checks the pipeline's own records (`PIPELINE-STATUS.md`, reviews, notes on related
|
|
192
|
+
papers) and the paper folder's layout.
|
|
193
|
+
- A deliberate exception is a comment on the line above:
|
|
213
194
|
`% eslint-disable-next-line paper/leading-zero -- quoted from the reviewer`.
|
|
214
|
-
- Errors fail the run; warnings only print, unless you pass `--max-warnings <n>`.
|
|
215
195
|
|
|
216
196
|
Every check: [`docs/rules.md`](docs/rules.md). How build compiles:
|
|
217
197
|
[`docs/configuration.md`](docs/configuration.md#how-paperlint-build-compiles-a-paper).
|
|
218
198
|
|
|
219
199
|
## 🧩 Configuration
|
|
220
200
|
|
|
221
|
-
Two levels, one file name, both optional
|
|
201
|
+
Two levels, one file name, both optional; a paper's file overrides the project's key by key, and
|
|
202
|
+
**an unknown key is an error**, so a typo cannot silently turn a setting off.
|
|
222
203
|
|
|
223
204
|
```
|
|
224
|
-
paperlint.json the project: papersDir, rules, defaults for every paper
|
|
205
|
+
paperlint.json the project: papersDir (default papers), rules, defaults for every paper
|
|
225
206
|
papers/my-paper/paperlint.json one paper: its venue preset ("extends"), its kind, its own rules
|
|
226
207
|
```
|
|
227
208
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
- **An unknown key is an error**, so a typo cannot silently turn a setting off.
|
|
232
|
-
- A paper that moves between venues keeps a list of attempts, `cycles`, instead of `"extends"`; each
|
|
233
|
-
records its deadlines as read from the portal and the call, plus any override the chairs granted,
|
|
234
|
-
and the earlier reading binds ([`docs/configuration.md`](docs/configuration.md#deadlines)).
|
|
235
|
-
- **Deadlines of a shipped venue preset stay current on their own:** paperlint's repository re-reads
|
|
236
|
-
each preset's portal daily and releases a patch when a date moves, so your normal dependency update
|
|
237
|
-
(Dependabot, Renovate) brings it. `paperlint lint` itself never reads the clock or the network.
|
|
238
|
-
|
|
239
|
-
Every key: [`docs/configuration.md`](docs/configuration.md). Already use ESLint for other files? See
|
|
240
|
-
[`docs/configuration.md`](docs/configuration.md#using-the-rules-from-an-existing-eslint-config).
|
|
209
|
+
Shipped venue presets keep their deadlines current: a date that moves is released as a patch, so your
|
|
210
|
+
normal dependency update brings it. Every key, a paper that moves between venues (`cycles`), and
|
|
211
|
+
using the rules from your own ESLint config: [`docs/configuration.md`](docs/configuration.md).
|
|
241
212
|
|
|
242
213
|
### ➕ Add a venue that isn't listed
|
|
243
214
|
|
|
@@ -267,51 +238,17 @@ Give the path from where you run the command; `new` rewrites it relative to the
|
|
|
267
238
|
|
|
268
239
|
## 🤖 Run it in CI
|
|
269
240
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
| job | time | checks |
|
|
273
|
-
| -------------------------- | ----------------------------- | ------------------------------------------ |
|
|
274
|
-
| build, then lint | ~3 min first run, then cached | everything |
|
|
275
|
-
| lint only (`init` adds it) | seconds | the source; not pages, fonts or references |
|
|
276
|
-
|
|
277
|
-
Lint judges pages, fonts and references from what build measured, so the full job builds first:
|
|
278
|
-
|
|
279
|
-
```yaml
|
|
280
|
-
name: papers
|
|
281
|
-
on: [push, pull_request]
|
|
282
|
-
jobs:
|
|
283
|
-
papers:
|
|
284
|
-
runs-on: ubuntu-latest
|
|
285
|
-
steps:
|
|
286
|
-
- uses: actions/checkout@v4
|
|
287
|
-
- uses: actions/setup-node@v4
|
|
288
|
-
with:
|
|
289
|
-
node-version: 22
|
|
290
|
-
- run: npm ci
|
|
291
|
-
- uses: actions/cache@v4
|
|
292
|
-
with:
|
|
293
|
-
path: ~/.cache/paperlint/texlive
|
|
294
|
-
key: texlive-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
|
|
295
|
-
- run: npx paperlint toolchain
|
|
296
|
-
- run: npx paperlint build --all
|
|
297
|
-
- run: npx paperlint lint
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
Lint only — each paper then gets one warning that it was not built. Call the action from the
|
|
301
|
-
installed package, after `npm ci`: the action and the CLI it runs are then one revision, pinned by
|
|
302
|
-
your lockfile.
|
|
241
|
+
`init` offers a lint-only workflow (seconds; skips pages, fonts and references). The full job builds
|
|
242
|
+
first (~3 min, then cached) and checks everything:
|
|
303
243
|
|
|
304
244
|
```yaml
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
node-version: 22
|
|
310
|
-
- run: npm ci
|
|
311
|
-
- uses: ./node_modules/paperlint
|
|
245
|
+
- run: npm ci
|
|
246
|
+
- run: npx paperlint toolchain
|
|
247
|
+
- run: npx paperlint build --all
|
|
248
|
+
- run: npx paperlint lint
|
|
312
249
|
```
|
|
313
250
|
|
|
314
|
-
|
|
251
|
+
Both workflows in full, with the TeX Live cache: [`docs/ci.md`](docs/ci.md).
|
|
315
252
|
|
|
316
253
|
## ❓ FAQ
|
|
317
254
|
|
|
@@ -329,6 +266,9 @@ Only `paperlint lint --fix`, and only three rules: `paper/section-word` (`§`
|
|
|
329
266
|
## 📚 Docs
|
|
330
267
|
|
|
331
268
|
- [`docs/skills.md`](docs/skills.md) — every skill, by stage
|
|
269
|
+
- [`docs/layout.md`](docs/layout.md) — the pipeline's flow, where each stage writes, and what checks it when
|
|
270
|
+
- [`docs/venues.md`](docs/venues.md) — a venue's corpus of accepted papers, presets and cards: three places, three jobs
|
|
271
|
+
- [`docs/ci.md`](docs/ci.md) — the two CI workflows, in full
|
|
332
272
|
- [`docs/install.md`](docs/install.md) — what `init` does, the hooks, package managers, troubleshooting
|
|
333
273
|
- [`docs/configuration.md`](docs/configuration.md) — every setting, how `build` compiles, using your own ESLint
|
|
334
274
|
- [`docs/rules.md`](docs/rules.md) — every check, venue presets, recording a submitted PDF
|
package/dist/build.d.ts
CHANGED
|
@@ -133,7 +133,7 @@ export declare const measureStep: BuildStep;
|
|
|
133
133
|
* `_build/references.json` for the offline lint rules (`reference-rules.ts`). OPTIONAL and NEVER
|
|
134
134
|
* FAILING: without network the PDF is still built, and the record says `not-checked`.
|
|
135
135
|
*
|
|
136
|
-
* What the services answered is read from and added to `<paper>/repro/references-cache.json`
|
|
136
|
+
* What the services answered is read from and added to `<paper>/repro/data/references-cache.json`
|
|
137
137
|
* (`references.ts`, `domain/lookup-cache.ts`), committed with the paper, so only a new or edited
|
|
138
138
|
* entry is asked, and an unchanged bibliography is checked with no network at all (#107).
|
|
139
139
|
*/
|
package/dist/build.js
CHANGED
|
@@ -420,7 +420,7 @@ export const measureStep = {
|
|
|
420
420
|
* `_build/references.json` for the offline lint rules (`reference-rules.ts`). OPTIONAL and NEVER
|
|
421
421
|
* FAILING: without network the PDF is still built, and the record says `not-checked`.
|
|
422
422
|
*
|
|
423
|
-
* What the services answered is read from and added to `<paper>/repro/references-cache.json`
|
|
423
|
+
* What the services answered is read from and added to `<paper>/repro/data/references-cache.json`
|
|
424
424
|
* (`references.ts`, `domain/lookup-cache.ts`), committed with the paper, so only a new or edited
|
|
425
425
|
* entry is asked, and an unchanged bibliography is checked with no network at all (#107).
|
|
426
426
|
*/
|
package/dist/cli.d.ts
CHANGED
|
@@ -146,7 +146,7 @@ export declare function toPaths(papers: unknown): string[];
|
|
|
146
146
|
* real verdict (2 included) passes through. Silent degradation is worse than explicit degradation,
|
|
147
147
|
* but blocking everything is worse than both.
|
|
148
148
|
*/
|
|
149
|
-
export declare function runHook(name: string | undefined, { err, run, resolve, }?: {
|
|
149
|
+
export declare function runHook(name: string | undefined, { err, run, resolve, stdin, out, }?: {
|
|
150
150
|
err?: typeof console.error;
|
|
151
151
|
/** Runs the hook with the terminal's streams: `spawnSync` is one, and only its status is read. */
|
|
152
152
|
run?: (file: string, args: readonly string[], options: {
|
|
@@ -155,6 +155,10 @@ export declare function runHook(name: string | undefined, { err, run, resolve, }
|
|
|
155
155
|
readonly status: number | null;
|
|
156
156
|
};
|
|
157
157
|
resolve?: (spec: string) => string;
|
|
158
|
+
/** The hook's payload, as Claude Code sends it on stdin. */
|
|
159
|
+
stdin?: () => string;
|
|
160
|
+
/** Where an in-process hook's output goes: stdout, which Claude Code reads. */
|
|
161
|
+
out?: (text: string) => void;
|
|
158
162
|
}): number;
|
|
159
163
|
/**
|
|
160
164
|
* Create one paper and lint it — shared by `paperlint new` and `paperlint init --paper`, so the two cannot
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAS7C,OAAO,EAAE,MAAM,EAAE,MAAM,+CAA+C,CAAC;AAEvE,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAapE,OAAO,EAAyB,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAwDrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAoBnE,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAGL,IAAI,EAEL,MAAM,WAAW,CAAC;AACnB,OAAO,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -71,7 +71,7 @@ import { paperPreset, settingsProblemLine, presetProblemText, resolvePreset, shi
|
|
|
71
71
|
import { mergeRequirements, declaredUnion, requirementsFor, NO_REQUIREMENTS, } from "./tex-requirements.js";
|
|
72
72
|
import { doctor } from "./doctor.js";
|
|
73
73
|
import { askOnTerminal, askOrDefault, init, processInteractivity, } from "./init.js";
|
|
74
|
-
import { newPaper, reportNewPaper } from "./new-paper.js";
|
|
74
|
+
import { newPaper, reportNewPaper, STATUS_FILE, } from "./new-paper.js";
|
|
75
75
|
// The one source for the consumer's config key lives in lib/ (the ESLint rules and the skill scripts
|
|
76
76
|
// import it too). It is imported compiled, from dist/, the one path that resolves the same from src/
|
|
77
77
|
// and from dist/ — see CONTRIBUTING.md.
|
|
@@ -80,9 +80,13 @@ import { parseRuleBlocks, parseRuleEntries, shippedRuleIds, unknownKeys, } from
|
|
|
80
80
|
export { init };
|
|
81
81
|
export { nextSteps } from "./init.js";
|
|
82
82
|
import paperStages from "#eslint-rules/paper-stages";
|
|
83
|
+
import preregCoverage from "#eslint-rules/prereg-coverage";
|
|
83
84
|
import cycleStage from "#eslint-rules/cycle-stage";
|
|
84
85
|
import { CYCLE_RULE_LEVELS, cycleRules } from "./cycle-rules.js";
|
|
85
86
|
import { FOLDER_VENUE_RULE_LEVELS, folderVenueRules, } from "./folder-venue-rule.js";
|
|
87
|
+
import { FOLDER_LAYOUT_RULE_LEVELS, folderLayoutRules, } from "./folder-layout-rule.js";
|
|
88
|
+
import { folderLayoutHook } from "./folder-layout-hook.js";
|
|
89
|
+
import { REPRO_LAYOUT_RULE_LEVELS, reproLayoutRules, } from "./repro-layout-rule.js";
|
|
86
90
|
import typography from "#eslint-rules/paper-typography";
|
|
87
91
|
import texBuild from "#eslint-rules/tex-build";
|
|
88
92
|
import reviewFrontmatter from "#eslint-rules/review-frontmatter";
|
|
@@ -90,6 +94,7 @@ import siblingFrontmatter from "#eslint-rules/sibling-frontmatter";
|
|
|
90
94
|
import pdfRules from "#eslint-rules/pdf-last-page-balance";
|
|
91
95
|
import { messageOf } from "./domain/text.js";
|
|
92
96
|
import { isRecord } from "./domain/record.js";
|
|
97
|
+
import { ESCAPE_HATCH } from "./domain/paper-layout.js";
|
|
93
98
|
/** The shipped venue presets, read from the package's venues directory — the one list. */
|
|
94
99
|
const SHIPPED_VENUES = () => shippedPresets(presetsDir());
|
|
95
100
|
const USAGE = `paperlint — machine-checkable gates for a paper kept in git
|
|
@@ -251,6 +256,8 @@ const FRAGMENT_RULES = {
|
|
|
251
256
|
"future-promise": texBuild["future-promise"],
|
|
252
257
|
"heading-case": REGISTER_AND_CASE_RULES["heading-case"],
|
|
253
258
|
};
|
|
259
|
+
/** What the two layout rules read with: the disk and its directory listings. */
|
|
260
|
+
const LAYOUT_RULE_DEPS = { files: nodeFiles, listDir: nodeListDir };
|
|
254
261
|
/** What the talk rules read with: the disk, the shipped presets, and the three media readers. */
|
|
255
262
|
const TALK_RULE_DEPS = {
|
|
256
263
|
files: nodeFiles,
|
|
@@ -278,7 +285,9 @@ export function buildConfig(opts = {}, texLanguage) {
|
|
|
278
285
|
// its files carry every marker a paper does. Flat config does NOT ignore dot-directories by
|
|
279
286
|
// default (only `node_modules/` and `.git/`), so without this block `paperlint lint` would lint the
|
|
280
287
|
// template as a paper — and a richer template with placeholder stages would fail the run.
|
|
281
|
-
|
|
288
|
+
// The same for a paper's `extra/`: the layout promises it is never judged, and an archived
|
|
289
|
+
// snapshot there carries a paper's markers too, so its folder read as a paper root with strays.
|
|
290
|
+
{ ignores: ["**/.template/", `**/${ESCAPE_HATCH}/`] },
|
|
282
291
|
// The `pdf` plugin is registered for EVERY file. A consumer's block (`rules`, appended below)
|
|
283
292
|
// may name its rules for a glob that also matches markdown files; with the plugin defined only
|
|
284
293
|
// beside `paper.tex`, ESLint would refuse those files with "could not find plugin". Every rule
|
|
@@ -307,8 +316,12 @@ export function buildConfig(opts = {}, texLanguage) {
|
|
|
307
316
|
rules: {
|
|
308
317
|
...paperStages.rules,
|
|
309
318
|
...folderVenueRules({ files: nodeFiles, venuesDir: presetsDir() }),
|
|
319
|
+
...folderLayoutRules(LAYOUT_RULE_DEPS),
|
|
310
320
|
},
|
|
311
321
|
},
|
|
322
|
+
repro: { rules: reproLayoutRules(LAYOUT_RULE_DEPS) },
|
|
323
|
+
// Registered, on for no paper: `"study/declare-sample": "error"` in paperlint.json turns it on.
|
|
324
|
+
study: { rules: preregCoverage.rules },
|
|
312
325
|
cycle: { rules: CYCLE_STATUS_RULES },
|
|
313
326
|
},
|
|
314
327
|
...md,
|
|
@@ -316,6 +329,8 @@ export function buildConfig(opts = {}, texLanguage) {
|
|
|
316
329
|
"paper/stages": "error",
|
|
317
330
|
"paper/source": "error",
|
|
318
331
|
...FOLDER_VENUE_RULE_LEVELS,
|
|
332
|
+
...FOLDER_LAYOUT_RULE_LEVELS,
|
|
333
|
+
...REPRO_LAYOUT_RULE_LEVELS,
|
|
319
334
|
...CYCLE_RULE_LEVELS,
|
|
320
335
|
},
|
|
321
336
|
},
|
|
@@ -516,7 +531,7 @@ export const OPTIONAL_RULES = new Set([...SHIPPED_RULES].filter((id) => !buildCo
|
|
|
516
531
|
* 🔴 AN OPTIONAL RULE THAT IS ON AND REACHES NO PAPER IS A GREEN ZERO. A `files` glob that matches
|
|
517
532
|
* nothing — a typo, a path relative to the wrong directory — leaves the rule never invoked, and a
|
|
518
533
|
* rule that never runs reports exactly like a rule that passed. So for every optional rule the
|
|
519
|
-
* consumer turned on, some `paper.tex` must actually have it enabled; the ones none has are
|
|
534
|
+
* consumer turned on, some paper's `paper.tex` or `PIPELINE-STATUS.md` must actually have it enabled; the ones none has are
|
|
520
535
|
* returned, for the caller to refuse.
|
|
521
536
|
*
|
|
522
537
|
* `files` is every `paper.tex` the guard may count: the ones this run linted AND every paper of the
|
|
@@ -533,9 +548,14 @@ export async function silentOptionalRules(eslint, files, opts) {
|
|
|
533
548
|
.filter(([id, e]) => OPTIONAL_RULES.has(id) && isOn(e))
|
|
534
549
|
.map(([id]) => id)));
|
|
535
550
|
const reached = new Set();
|
|
536
|
-
|
|
551
|
+
// A paper's rules reach it through two files: `paper.tex` and the `PIPELINE-STATUS.md` beside it,
|
|
552
|
+
// where the rules about the paper folder itself run.
|
|
553
|
+
const probes = [
|
|
554
|
+
...new Set(files.filter((p) => basename(p) === MAIN)),
|
|
555
|
+
].flatMap((f) => [f, join(dirname(f), STATUS_FILE)]);
|
|
556
|
+
for (const probe of probes) {
|
|
537
557
|
// Undefined for a file outside ESLint's cwd or scope: it reaches nothing.
|
|
538
|
-
const cfg = ComputedConfig.parse(await eslint.calculateConfigForFile(
|
|
558
|
+
const cfg = ComputedConfig.parse(await eslint.calculateConfigForFile(probe));
|
|
539
559
|
// Reached is "some block names the rule for this paper", on OR off: a paper that turns off what
|
|
540
560
|
// its venue preset turned on is the documented way out, and reads the same as a glob that missed
|
|
541
561
|
// only if "off" is counted as "not there" — which refused every such paper.
|
|
@@ -877,6 +897,27 @@ export function toPaths(papers) {
|
|
|
877
897
|
return papers.filter((x) => typeof x === "string" && x.trim() !== "");
|
|
878
898
|
return [];
|
|
879
899
|
}
|
|
900
|
+
/**
|
|
901
|
+
* Hooks that run inside this process instead of as a vigiles program, each because it must call the
|
|
902
|
+
* package's own code (`src/folder-layout-hook.ts` says why for its one). Advisory: whatever goes
|
|
903
|
+
* wrong, the tool call has already happened, so it says so on stderr and exits 0.
|
|
904
|
+
*/
|
|
905
|
+
const IN_PROCESS_HOOKS = {
|
|
906
|
+
"paper-folder-layout": ({ err, stdin, out }) => {
|
|
907
|
+
try {
|
|
908
|
+
const said = folderLayoutHook(JSON.parse(stdin()), {
|
|
909
|
+
files: nodeFiles,
|
|
910
|
+
listDir: nodeListDir,
|
|
911
|
+
});
|
|
912
|
+
if (said !== null)
|
|
913
|
+
out(`${JSON.stringify(said)}\n`);
|
|
914
|
+
}
|
|
915
|
+
catch (e) {
|
|
916
|
+
err(`paperlint: the paper-folder-layout hook did not run: ${messageOf(e)}`);
|
|
917
|
+
}
|
|
918
|
+
return 0;
|
|
919
|
+
},
|
|
920
|
+
};
|
|
880
921
|
/**
|
|
881
922
|
* `paperlint hook <name>` — run an editor hook. It exists for ONE thing: so that the wiring does not
|
|
882
923
|
* address the runtime from the project root.
|
|
@@ -912,11 +953,16 @@ export function runHook(name, { err = console.error, run = spawnSync,
|
|
|
912
953
|
// This is neither our oversight nor their bug: a closed export map is normal practice. So we
|
|
913
954
|
// resolve the root entry ("." → dist/test.js), take its directory and put `cli.js` next to it —
|
|
914
955
|
// the very file the package itself declares as its `bin`.
|
|
915
|
-
resolve = (spec) => createRequire(import.meta.url).resolve(spec),
|
|
956
|
+
resolve = (spec) => createRequire(import.meta.url).resolve(spec), stdin = () => readFileSync(0, "utf8"), out = (text) => {
|
|
957
|
+
process.stdout.write(text);
|
|
958
|
+
}, } = {}) {
|
|
916
959
|
if (!name) {
|
|
917
960
|
err(`\`hook\` needs a name, e.g. \`paperlint hook paper-edit-guard\``);
|
|
918
961
|
return 2;
|
|
919
962
|
}
|
|
963
|
+
const inProcess = IN_PROCESS_HOOKS[name];
|
|
964
|
+
if (inProcess !== undefined)
|
|
965
|
+
return inProcess({ err, stdin, out });
|
|
920
966
|
const program = fileURLToPath(new URL(`../hooks/${name}.hook.mjs`, import.meta.url));
|
|
921
967
|
if (!existsSync(program)) {
|
|
922
968
|
err(`unknown hook \`${name}\` — no such program at ${program}`);
|