paperlint 3.2.0 → 4.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.
Files changed (442) hide show
  1. package/.github/workflows/ci.yml +16 -23
  2. package/CLAUDE.md +33 -13
  3. package/CONTRIBUTING.md +23 -7
  4. package/README.md +11 -8
  5. package/dist/adapters/banal/failure.d.ts +1 -7
  6. package/dist/adapters/banal/failure.d.ts.map +1 -1
  7. package/dist/adapters/banal/failure.js +5 -7
  8. package/dist/adapters/banal/failure.js.map +1 -1
  9. package/dist/adapters/banal/index.d.ts.map +1 -1
  10. package/dist/adapters/banal/index.js +8 -11
  11. package/dist/adapters/banal/index.js.map +1 -1
  12. package/dist/adapters/banal/install.d.ts.map +1 -1
  13. package/dist/adapters/banal/install.js +4 -3
  14. package/dist/adapters/banal/install.js.map +1 -1
  15. package/dist/adapters/banal/invocation.d.ts +10 -5
  16. package/dist/adapters/banal/invocation.d.ts.map +1 -1
  17. package/dist/adapters/banal/invocation.js +13 -1
  18. package/dist/adapters/banal/invocation.js.map +1 -1
  19. package/dist/adapters/banal/locate.d.ts +12 -9
  20. package/dist/adapters/banal/locate.d.ts.map +1 -1
  21. package/dist/adapters/banal/locate.js +19 -12
  22. package/dist/adapters/banal/locate.js.map +1 -1
  23. package/dist/adapters/banal/run.d.ts +10 -1
  24. package/dist/adapters/banal/run.d.ts.map +1 -1
  25. package/dist/adapters/banal/run.js +8 -4
  26. package/dist/adapters/banal/run.js.map +1 -1
  27. package/dist/adapters/banal/settings.d.ts +1 -1
  28. package/dist/adapters/banal/settings.d.ts.map +1 -1
  29. package/dist/adapters/banal/settings.js +2 -1
  30. package/dist/adapters/banal/settings.js.map +1 -1
  31. package/dist/adapters/latex/document-class.d.ts +22 -0
  32. package/dist/adapters/latex/document-class.d.ts.map +1 -0
  33. package/dist/adapters/latex/document-class.js +102 -0
  34. package/dist/adapters/latex/document-class.js.map +1 -0
  35. package/dist/adapters/latex/hidden.d.ts +16 -0
  36. package/dist/adapters/latex/hidden.d.ts.map +1 -0
  37. package/dist/adapters/latex/hidden.js +49 -0
  38. package/dist/adapters/latex/hidden.js.map +1 -0
  39. package/dist/adapters/latex/index.d.ts +29 -0
  40. package/dist/adapters/latex/index.d.ts.map +1 -0
  41. package/dist/adapters/latex/index.js +18 -0
  42. package/dist/adapters/latex/index.js.map +1 -0
  43. package/dist/adapters/latex/nodes.d.ts +54 -0
  44. package/dist/adapters/latex/nodes.d.ts.map +1 -0
  45. package/dist/adapters/latex/nodes.js +93 -0
  46. package/dist/adapters/latex/nodes.js.map +1 -0
  47. package/dist/adapters/latex/outline.d.ts +10 -0
  48. package/dist/adapters/latex/outline.d.ts.map +1 -0
  49. package/dist/adapters/latex/outline.js +39 -0
  50. package/dist/adapters/latex/outline.js.map +1 -0
  51. package/dist/adapters/latex/parse.d.ts +42 -0
  52. package/dist/adapters/latex/parse.d.ts.map +1 -0
  53. package/dist/adapters/latex/parse.js +94 -0
  54. package/dist/adapters/latex/parse.js.map +1 -0
  55. package/dist/adapters/latex/rendered.d.ts +10 -0
  56. package/dist/adapters/latex/rendered.d.ts.map +1 -0
  57. package/dist/adapters/latex/rendered.js +39 -0
  58. package/dist/adapters/latex/rendered.js.map +1 -0
  59. package/dist/adapters/memory/index.d.ts.map +1 -1
  60. package/dist/adapters/memory/index.js +8 -2
  61. package/dist/adapters/memory/index.js.map +1 -1
  62. package/dist/adapters/node/process.io.d.ts +6 -3
  63. package/dist/adapters/node/process.io.d.ts.map +1 -1
  64. package/dist/adapters/node/process.io.js.map +1 -1
  65. package/dist/adapters/node/workspace.io.d.ts.map +1 -1
  66. package/dist/adapters/node/workspace.io.js +4 -2
  67. package/dist/adapters/node/workspace.io.js.map +1 -1
  68. package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
  69. package/dist/adapters/pdfjs/fill.js +12 -12
  70. package/dist/adapters/pdfjs/fill.js.map +1 -1
  71. package/dist/adapters/references/index.d.ts.map +1 -1
  72. package/dist/adapters/references/index.js +4 -2
  73. package/dist/adapters/references/index.js.map +1 -1
  74. package/dist/build-engine.d.ts +1 -1
  75. package/dist/build-engine.d.ts.map +1 -1
  76. package/dist/build-engine.js +8 -1
  77. package/dist/build-engine.js.map +1 -1
  78. package/dist/build.d.ts +2 -3
  79. package/dist/build.d.ts.map +1 -1
  80. package/dist/build.js +9 -10
  81. package/dist/build.js.map +1 -1
  82. package/dist/caller-path.d.ts +3 -0
  83. package/dist/caller-path.d.ts.map +1 -0
  84. package/dist/caller-path.js +13 -0
  85. package/dist/caller-path.js.map +1 -0
  86. package/dist/cli.d.ts +10 -17
  87. package/dist/cli.d.ts.map +1 -1
  88. package/dist/cli.js +91 -17
  89. package/dist/cli.js.map +1 -1
  90. package/dist/doctor.d.ts +12 -3
  91. package/dist/doctor.d.ts.map +1 -1
  92. package/dist/doctor.js +2 -1
  93. package/dist/doctor.js.map +1 -1
  94. package/dist/domain/paths.d.ts +8 -2
  95. package/dist/domain/paths.d.ts.map +1 -1
  96. package/dist/domain/paths.js +12 -1
  97. package/dist/domain/paths.js.map +1 -1
  98. package/dist/domain/ready.d.ts +24 -0
  99. package/dist/domain/ready.d.ts.map +1 -0
  100. package/dist/domain/ready.js +11 -0
  101. package/dist/domain/ready.js.map +1 -0
  102. package/dist/domain/record.d.ts +8 -0
  103. package/dist/domain/record.d.ts.map +1 -0
  104. package/dist/domain/record.js +8 -0
  105. package/dist/domain/record.js.map +1 -0
  106. package/dist/domain/sha256.d.ts.map +1 -1
  107. package/dist/domain/sha256.js +4 -2
  108. package/dist/domain/sha256.js.map +1 -1
  109. package/dist/domain/tex-document.d.ts +84 -0
  110. package/dist/domain/tex-document.d.ts.map +1 -0
  111. package/dist/domain/tex-document.js +43 -0
  112. package/dist/domain/tex-document.js.map +1 -0
  113. package/dist/engine.d.ts +23 -22
  114. package/dist/engine.d.ts.map +1 -1
  115. package/dist/engine.js.map +1 -1
  116. package/dist/eslint-rules/bib-reachable-entry.d.ts +1 -1
  117. package/dist/eslint-rules/bib-reachable-entry.js.map +1 -1
  118. package/dist/eslint-rules/install-path-literals.d.ts +2 -2
  119. package/dist/eslint-rules/install-path-literals.js.map +1 -1
  120. package/dist/eslint-rules/paper-research-question.d.ts +1 -1
  121. package/dist/eslint-rules/paper-research-question.js.map +1 -1
  122. package/dist/eslint-rules/paper-stages.d.ts +2 -2
  123. package/dist/eslint-rules/paper-stages.js.map +1 -1
  124. package/dist/eslint-rules/paper-typography.d.ts +6 -6
  125. package/dist/eslint-rules/paper-typography.js.map +1 -1
  126. package/dist/eslint-rules/pdf-last-page-balance.d.ts +1 -1
  127. package/dist/eslint-rules/pdf-last-page-balance.js.map +1 -1
  128. package/dist/eslint-rules/review-frontmatter.d.ts +2 -2
  129. package/dist/eslint-rules/review-frontmatter.js.map +1 -1
  130. package/dist/eslint-rules/sibling-frontmatter.d.ts +1 -1
  131. package/dist/eslint-rules/sibling-frontmatter.d.ts.map +1 -1
  132. package/dist/eslint-rules/temp-root-realpath.d.ts +1 -1
  133. package/dist/eslint-rules/temp-root-realpath.js.map +1 -1
  134. package/dist/eslint-rules/tex-build.d.ts +2 -2
  135. package/dist/eslint-rules/tex-build.js.map +1 -1
  136. package/dist/facts-file.d.ts.map +1 -1
  137. package/dist/facts-file.js +31 -28
  138. package/dist/facts-file.js.map +1 -1
  139. package/dist/hooks-settings.d.ts.map +1 -1
  140. package/dist/hooks-settings.js +17 -2
  141. package/dist/hooks-settings.js.map +1 -1
  142. package/dist/init.d.ts +3 -4
  143. package/dist/init.d.ts.map +1 -1
  144. package/dist/init.js +18 -8
  145. package/dist/init.js.map +1 -1
  146. package/dist/lib/paper-config.d.ts.map +1 -1
  147. package/dist/lib/paper-config.js +1 -5
  148. package/dist/lib/paper-config.js.map +1 -1
  149. package/dist/new-paper.d.ts +5 -0
  150. package/dist/new-paper.d.ts.map +1 -1
  151. package/dist/new-paper.js +8 -1
  152. package/dist/new-paper.js.map +1 -1
  153. package/dist/package-dirs.d.ts +5 -0
  154. package/dist/package-dirs.d.ts.map +1 -0
  155. package/dist/package-dirs.js +14 -0
  156. package/dist/package-dirs.js.map +1 -0
  157. package/dist/paper-files.d.ts +4 -4
  158. package/dist/paper-files.d.ts.map +1 -1
  159. package/dist/paper-files.js +6 -6
  160. package/dist/paper-files.js.map +1 -1
  161. package/dist/paper-settings.js +3 -3
  162. package/dist/paper-settings.js.map +1 -1
  163. package/dist/pdf-facts.d.ts +51 -1
  164. package/dist/pdf-facts.d.ts.map +1 -1
  165. package/dist/pdf-facts.js +23 -15
  166. package/dist/pdf-facts.js.map +1 -1
  167. package/dist/pdf-geometry.d.ts.map +1 -1
  168. package/dist/pdf-geometry.js +2 -3
  169. package/dist/pdf-geometry.js.map +1 -1
  170. package/dist/ports/latex.d.ts +24 -0
  171. package/dist/ports/latex.d.ts.map +1 -0
  172. package/dist/ports/latex.js +2 -0
  173. package/dist/ports/latex.js.map +1 -0
  174. package/dist/ports/tool-installer.d.ts +2 -14
  175. package/dist/ports/tool-installer.d.ts.map +1 -1
  176. package/dist/presets.d.ts +15 -2
  177. package/dist/presets.d.ts.map +1 -1
  178. package/dist/presets.js +35 -10
  179. package/dist/presets.js.map +1 -1
  180. package/dist/references.d.ts.map +1 -1
  181. package/dist/references.js +4 -4
  182. package/dist/references.js.map +1 -1
  183. package/dist/rules-config.d.ts +26 -1
  184. package/dist/rules-config.d.ts.map +1 -1
  185. package/dist/rules-config.js +9 -9
  186. package/dist/rules-config.js.map +1 -1
  187. package/dist/tex-requirements.d.ts +15 -0
  188. package/dist/tex-requirements.d.ts.map +1 -1
  189. package/dist/tex-requirements.js +28 -10
  190. package/dist/tex-requirements.js.map +1 -1
  191. package/dist/tex-venue-rules.d.ts +83 -0
  192. package/dist/tex-venue-rules.d.ts.map +1 -0
  193. package/dist/tex-venue-rules.js +254 -0
  194. package/dist/tex-venue-rules.js.map +1 -0
  195. package/dist/types.d.ts +5 -3
  196. package/dist/types.d.ts.map +1 -1
  197. package/dist/venue-rules.d.ts +1 -1
  198. package/dist/venue-rules.d.ts.map +1 -1
  199. package/dist/venue-rules.js +20 -7
  200. package/dist/venue-rules.js.map +1 -1
  201. package/docs/configuration.md +5 -4
  202. package/docs/e2e.md +69 -40
  203. package/docs/install.md +1 -1
  204. package/docs/prior-art/README.md +1 -1
  205. package/docs/prior-art/package-location.md +5 -5
  206. package/docs/prior-art/repro/README.md +1 -1
  207. package/docs/prior-art/test-tooling.md +21 -10
  208. package/docs/rules/tex/required-section.md +73 -0
  209. package/docs/rules/tex/template.md +62 -0
  210. package/docs/rules/tex/venue-leftover.md +71 -0
  211. package/docs/rules.md +70 -48
  212. package/docs/testing.md +17 -4
  213. package/docs/texlive-install-decision.md +1 -1
  214. package/docs/toolchain.md +2 -2
  215. package/eslint-rules/bib-reachable-entry.ts +1 -1
  216. package/eslint-rules/install-path-literals.ts +2 -2
  217. package/eslint-rules/paper-research-question.ts +1 -1
  218. package/eslint-rules/paper-stages.ts +2 -2
  219. package/eslint-rules/paper-typography.ts +2 -2
  220. package/eslint-rules/pdf-last-page-balance.ts +1 -1
  221. package/eslint-rules/review-frontmatter.ts +1 -1
  222. package/eslint-rules/temp-root-realpath.ts +1 -1
  223. package/eslint-rules/tex-build.ts +2 -2
  224. package/eslint.config.mjs +162 -15
  225. package/fixtures/build-e2e/acmart/expect.json +26 -0
  226. package/fixtures/build-e2e/aidc/PIPELINE-STATUS.md +3 -0
  227. package/fixtures/build-e2e/aidc/expect.json +14 -0
  228. package/fixtures/build-e2e/aidc/paper.tex +46 -0
  229. package/fixtures/build-e2e/aidc/paperlint.json +1 -0
  230. package/fixtures/build-e2e/broken/expect.json +11 -0
  231. package/fixtures/build-e2e/cite/expect.json +27 -0
  232. package/fixtures/build-e2e/empty/expect.json +12 -0
  233. package/fixtures/build-e2e/fallback/expect.json +53 -0
  234. package/fixtures/build-e2e/guards/expect.json +6 -0
  235. package/fixtures/build-e2e/no-source/expect.json +10 -0
  236. package/fixtures/build-e2e/unbalanced/expect.json +60 -0
  237. package/fixtures/install-path-literals/clean.fixture.mjs +1 -1
  238. package/fixtures/install-path-literals/clean.md +2 -2
  239. package/fixtures/install-path-literals/defect.fixture.mjs +1 -1
  240. package/fixtures/install-path-literals/defect.md +2 -2
  241. package/fixtures/pdf-facts/README.md +1 -1
  242. package/fixtures/real-markdown-paper/baseline.ts +1 -1
  243. package/fixtures/render-paper/build-clean.sh +3 -5
  244. package/hooks/paper-status-gates.sh +9 -19
  245. package/lib/paper-config.harness.mjs +1 -1
  246. package/lib/paper-config.ts +1 -5
  247. package/lib/skill-checks.mjs +24 -381
  248. package/lib/skill-checks.test.mjs +63 -115
  249. package/lib/skill-corpus.mjs +10 -251
  250. package/lib/skill-corpus.test.mjs +2 -13
  251. package/lib/skill-trigger-cases.mjs +1 -1
  252. package/package.json +13 -2
  253. package/{skills/submit-paper/references/venues → presets}/acm-sigconf.jsonc +4 -1
  254. package/{skills/submit-paper/references/venues → presets}/agenticdev.jsonc +4 -0
  255. package/presets/aidc.jsonc +73 -0
  256. package/{skills/submit-paper/references/venues → presets}/aisec.jsonc +4 -0
  257. package/presets/ieee-conference.jsonc +66 -0
  258. package/{skills/submit-paper/references/venues → presets}/realm.jsonc +8 -1
  259. package/{skills/submit-paper/references/venues → presets/tex}/paper-guards.tex +1 -1
  260. package/{skills/submit-paper/references/venues → presets}/venue-profile.schema.json +30 -2
  261. package/scripts/check.harness.ts +64 -10
  262. package/scripts/check.test.ts +60 -13
  263. package/scripts/check.ts +83 -30
  264. package/scripts/coverage-config.test.ts +0 -1
  265. package/scripts/harness-api.frozen.json +0 -1
  266. package/scripts/test.test.ts +1 -1
  267. package/scripts/test.ts +1 -1
  268. package/skills/analyze-sibling-paper/SKILL.md +1 -39
  269. package/skills/analyze-sibling-paper/SKILL.md.spec.ts +0 -38
  270. package/skills/argument-arc/SKILL.md +2 -36
  271. package/skills/argument-arc/SKILL.md.spec.ts +1 -43
  272. package/skills/build-benchmark/SKILL.md +1 -41
  273. package/skills/build-benchmark/SKILL.md.spec.ts +0 -40
  274. package/skills/camera-ready/SKILL.md +1 -34
  275. package/skills/camera-ready/SKILL.md.spec.ts +0 -33
  276. package/skills/cold-read-diff/SKILL.md +1 -35
  277. package/skills/cold-read-diff/SKILL.md.spec.ts +0 -34
  278. package/skills/draft-paper/SKILL.md +2 -41
  279. package/skills/draft-paper/SKILL.md.spec.ts +1 -50
  280. package/skills/extend-paper/SKILL.md +2 -33
  281. package/skills/extend-paper/SKILL.md.spec.ts +1 -42
  282. package/skills/find-venue/SKILL.md +2 -38
  283. package/skills/find-venue/SKILL.md.spec.ts +1 -47
  284. package/skills/grade-paper-writing/SKILL.md +3 -38
  285. package/skills/grade-paper-writing/SKILL.md.spec.ts +2 -47
  286. package/skills/grade-paper-writing/prose-lint.mjs +2 -3
  287. package/skills/grade-paper-writing/prose-lint.test.ts +1 -1
  288. package/skills/harden-paper/SKILL.md +1 -39
  289. package/skills/harden-paper/SKILL.md.spec.ts +0 -38
  290. package/skills/map-prior-work/SKILL.md +1 -35
  291. package/skills/map-prior-work/SKILL.md.spec.ts +0 -34
  292. package/skills/osf-artifact-upload/osf-artifact-upload.harness.ts +3 -4
  293. package/skills/paper-adversarial-review/SKILL.md +2 -35
  294. package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -43
  295. package/skills/paper-pipeline/PIPELINE-MAP.md +2 -2
  296. package/skills/paper-pipeline/SKILL.md +3 -29
  297. package/skills/paper-pipeline/SKILL.md.spec.ts +2 -28
  298. package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +1 -1
  299. package/skills/paper-pipeline/paper-pipeline.harness.mjs +25 -40
  300. package/skills/paper-pipeline/references/acceptance-gate.md +1 -6
  301. package/skills/paper-pipeline/references/pipeline-status-template.md +8 -35
  302. package/skills/paper-pipeline/scripts/README.md +30 -225
  303. package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +4 -8
  304. package/skills/paper-pipeline/scripts/consumer.d.mts +0 -2
  305. package/skills/paper-pipeline/scripts/consumer.harness.mjs +6 -104
  306. package/skills/paper-pipeline/scripts/consumer.mjs +24 -119
  307. package/skills/paper-pipeline/scripts/generated-code.harness.mjs +2 -3
  308. package/skills/paper-pipeline/scripts/population-map.harness.mjs +3 -3
  309. package/skills/paper-status/SKILL.md +2 -42
  310. package/skills/paper-status/SKILL.md.spec.ts +1 -41
  311. package/skills/pc-panel-review/SKILL.md +2 -46
  312. package/skills/pc-panel-review/SKILL.md.spec.ts +1 -45
  313. package/skills/plan-paper-timeline/SKILL.md +2 -36
  314. package/skills/plan-paper-timeline/SKILL.md.spec.ts +0 -36
  315. package/skills/render-paper/SKILL.md +2 -38
  316. package/skills/render-paper/SKILL.md.spec.ts +1 -37
  317. package/skills/render-paper/check-render.sh +16 -63
  318. package/skills/render-paper/render-paper.harness.mjs +17 -59
  319. package/skills/research-ideate/SKILL.md +2 -39
  320. package/skills/research-ideate/SKILL.md.spec.ts +1 -47
  321. package/skills/study-accepted-papers/SKILL.md +1 -34
  322. package/skills/study-accepted-papers/SKILL.md.spec.ts +0 -33
  323. package/skills/submit-paper/SKILL.md +1 -33
  324. package/skills/submit-paper/SKILL.md.spec.ts +0 -32
  325. package/skills/sweep-design-space/SKILL.md +1 -38
  326. package/skills/sweep-design-space/SKILL.md.spec.ts +0 -37
  327. package/skills/tighten-paper/SKILL.md +2 -39
  328. package/skills/tighten-paper/SKILL.md.spec.ts +1 -47
  329. package/skills/tighten-paper/structure.mjs +4 -11
  330. package/skills/verify-citations/SKILL.md +2 -38
  331. package/skills/verify-citations/SKILL.md.spec.ts +1 -37
  332. package/src/adapters/banal/failure.ts +9 -10
  333. package/src/adapters/banal/index.test.ts +3 -3
  334. package/src/adapters/banal/index.ts +8 -10
  335. package/src/adapters/banal/install.ts +9 -4
  336. package/src/adapters/banal/invocation.test.ts +20 -8
  337. package/src/adapters/banal/invocation.ts +24 -9
  338. package/src/adapters/banal/locate.test.ts +2 -2
  339. package/src/adapters/banal/locate.ts +27 -21
  340. package/src/adapters/banal/output.test.ts +1 -1
  341. package/src/adapters/banal/run.test.ts +3 -3
  342. package/src/adapters/banal/run.ts +16 -10
  343. package/src/adapters/banal/settings.ts +2 -2
  344. package/src/adapters/banal/xml.test.ts +1 -1
  345. package/src/adapters/curl/download.test.ts +4 -4
  346. package/src/adapters/latex/document-class.ts +147 -0
  347. package/src/adapters/latex/hidden.ts +70 -0
  348. package/src/adapters/latex/index.test.ts +361 -0
  349. package/src/adapters/latex/index.ts +55 -0
  350. package/src/adapters/latex/nodes.ts +141 -0
  351. package/src/adapters/latex/outline.ts +64 -0
  352. package/src/adapters/latex/parse.ts +136 -0
  353. package/src/adapters/latex/rendered.ts +59 -0
  354. package/src/adapters/memory/index.ts +4 -3
  355. package/src/adapters/node/files.test.ts +5 -3
  356. package/src/adapters/node/process.io.ts +9 -2
  357. package/src/adapters/node/process.test.ts +4 -4
  358. package/src/adapters/node/workspace.io.ts +9 -4
  359. package/src/adapters/pdfjs/fill.test.ts +1 -1
  360. package/src/adapters/pdfjs/fill.ts +18 -10
  361. package/src/adapters/references/index.ts +7 -2
  362. package/src/build-engine.test.ts +11 -6
  363. package/src/build-engine.ts +10 -5
  364. package/src/build.harness.mjs +5 -7
  365. package/src/build.test.ts +10 -11
  366. package/src/build.ts +12 -13
  367. package/src/caller-path.ts +13 -0
  368. package/src/cli.harness.mjs +1 -3
  369. package/src/cli.test.ts +21 -16
  370. package/src/cli.ts +115 -33
  371. package/src/doctor.test.ts +1 -1
  372. package/src/doctor.ts +14 -5
  373. package/src/domain/paths.test.ts +15 -0
  374. package/src/domain/paths.ts +20 -1
  375. package/src/domain/ready.ts +31 -0
  376. package/src/domain/record.test.ts +20 -0
  377. package/src/domain/record.ts +12 -0
  378. package/src/domain/sha256.ts +5 -3
  379. package/src/domain/tex-document.test.ts +80 -0
  380. package/src/domain/tex-document.ts +110 -0
  381. package/src/engine.harness.mjs +1 -1
  382. package/src/engine.ts +30 -2
  383. package/src/facts-file.test.ts +4 -7
  384. package/src/facts-file.ts +45 -30
  385. package/src/hooks-settings.harness.ts +17 -0
  386. package/src/hooks-settings.ts +28 -4
  387. package/src/init.test.ts +25 -5
  388. package/src/init.ts +26 -12
  389. package/src/latex-loop.harness.ts +1 -1
  390. package/src/new-paper.harness.mjs +7 -23
  391. package/src/new-paper.test.ts +28 -3
  392. package/src/new-paper.ts +15 -3
  393. package/src/package-dirs.ts +17 -0
  394. package/src/paper-files.test.ts +22 -19
  395. package/src/paper-files.ts +16 -27
  396. package/src/paper-settings-commands.test.ts +6 -1
  397. package/src/paper-settings.test.ts +2 -2
  398. package/src/paper-settings.ts +5 -5
  399. package/src/pdf-facts.test.ts +16 -5
  400. package/src/pdf-facts.ts +79 -22
  401. package/src/pdf-geometry.ts +5 -3
  402. package/src/ports/latex.ts +29 -0
  403. package/src/ports/tool-installer.ts +2 -17
  404. package/src/presets.test.ts +65 -4
  405. package/src/presets.ts +65 -28
  406. package/src/references.test.ts +25 -19
  407. package/src/references.ts +4 -5
  408. package/src/rule-cases.test.ts +474 -0
  409. package/src/rule-docs.test.ts +159 -0
  410. package/src/rules-config.ts +40 -11
  411. package/src/tex-requirements.harness.ts +13 -23
  412. package/src/tex-requirements.test.ts +21 -0
  413. package/src/tex-requirements.ts +64 -14
  414. package/src/tex-venue-rules.test.ts +659 -0
  415. package/src/tex-venue-rules.ts +429 -0
  416. package/src/toolchain.harness.mjs +1 -1
  417. package/src/toolchain.test.ts +9 -5
  418. package/src/toolchain.ts +2 -2
  419. package/src/types.ts +5 -3
  420. package/src/venue-rules.test.ts +104 -8
  421. package/src/venue-rules.ts +22 -12
  422. package/templates/paper/paper.tex +2 -1
  423. package/skills/paper-pipeline/scripts/announce.mjs +0 -67
  424. package/skills/paper-pipeline/scripts/announce.test.mjs +0 -102
  425. package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +0 -51
  426. package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +0 -52
  427. package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +0 -6
  428. package/skills/paper-pipeline/scripts/fixtures/scorecard.ts +0 -100
  429. package/skills/paper-pipeline/scripts/ledger.cli.test.mjs +0 -302
  430. package/skills/paper-pipeline/scripts/ledger.mjs +0 -623
  431. package/skills/paper-pipeline/scripts/ledger.test.mjs +0 -254
  432. package/skills/paper-pipeline/scripts/pipeline-check.cli.test.mjs +0 -474
  433. package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +0 -328
  434. package/skills/paper-pipeline/scripts/pipeline-check.mjs +0 -750
  435. package/skills/paper-pipeline/scripts/pipeline-edges.mjs +0 -169
  436. package/skills/paper-pipeline/scripts/pipeline-edges.test.mjs +0 -73
  437. package/skills/paper-pipeline/scripts/run-mechanical.mjs +0 -648
  438. package/skills/paper-pipeline/scripts/run-mechanical.test.mjs +0 -468
  439. package/skills/paper-pipeline/scripts/status.mjs +0 -295
  440. package/skills/paper-pipeline/scripts/status.test.mjs +0 -232
  441. /package/{skills/submit-paper/references/venues → presets/tex}/agenticdev.tex +0 -0
  442. /package/{skills/submit-paper/references/venues → presets}/tex-base.jsonc +0 -0
@@ -39,11 +39,40 @@ export interface RuleBlock {
39
39
  readonly rules: Readonly<Record<string, RuleEntry>>;
40
40
  }
41
41
 
42
+ /**
43
+ * A plugin as a config block registers it, typed to what paperlint reads of it: its rules, and the
44
+ * languages it brings (`tex` brings LaTeX).
45
+ */
46
+ export interface ConfigPlugin {
47
+ readonly rules?: Readonly<Record<string, unknown>>;
48
+ readonly languages?: Readonly<Record<string, unknown>>;
49
+ }
50
+
51
+ /**
52
+ * An ESLint config block as paperlint builds and reads one. Not `Linter.Config`: ESLint types a
53
+ * plugin's rule as one that runs on ANY language, and paperlint's rules are written for the language
54
+ * they read (LaTeX, markdown), so none of them is assignable to it. ESLint checks a block when it
55
+ * loads it; `eslintConfig` in cli.ts is the one place these blocks become `Linter.Config`.
56
+ */
57
+ export interface ConfigBlock {
58
+ readonly name?: string;
59
+ readonly basePath?: string;
60
+ readonly files?: readonly (string | readonly string[])[];
61
+ readonly ignores?: readonly string[];
62
+ readonly plugins?: Readonly<Record<string, ConfigPlugin>>;
63
+ readonly language?: string;
64
+ readonly languageOptions?: Readonly<Record<string, unknown>>;
65
+ readonly linterOptions?: Readonly<Record<string, unknown>>;
66
+ readonly rules?: Readonly<Record<string, unknown>>;
67
+ }
68
+
42
69
  export type Parsed<T> =
43
70
  | { readonly ok: true; readonly value: T }
44
71
  | { readonly ok: false; readonly error: string };
45
72
 
46
- const SEVERITIES: readonly unknown[] = ["off", "warn", "error", 0, 1, 2];
73
+ const SEVERITIES: readonly Severity[] = ["off", "warn", "error", 0, 1, 2];
74
+ const isSeverity = (v: unknown): v is Severity =>
75
+ SEVERITIES.some((s) => s === v);
47
76
 
48
77
  const BLOCK_KEYS = new Set(["files", "ignores", "rules"]);
49
78
 
@@ -61,9 +90,10 @@ const isStringList = (v: unknown): v is string[] =>
61
90
 
62
91
  /** A severity, or `[severity, ...options]`. */
63
92
  function parseEntry(v: unknown): RuleEntry | null {
64
- const sev: unknown = Array.isArray(v) ? v[0] : v;
65
- if (!SEVERITIES.includes(sev)) return null;
66
- return v as RuleEntry;
93
+ const entry: readonly unknown[] = Array.isArray(v) ? v : [v];
94
+ const [sev, ...options] = entry;
95
+ if (!isSeverity(sev)) return null;
96
+ return Array.isArray(v) ? [sev, ...options] : sev;
67
97
  }
68
98
 
69
99
  /**
@@ -105,8 +135,8 @@ function badGlobs(o: Record<string, unknown>, at: string): string | null {
105
135
 
106
136
  /** The block's `files`/`ignores`, present only when given. */
107
137
  const globsOf = (o: Record<string, unknown>) => ({
108
- ...(o["files"] ? { files: o["files"] as string[] } : {}),
109
- ...(o["ignores"] ? { ignores: o["ignores"] as string[] } : {}),
138
+ ...(isStringList(o["files"]) ? { files: o["files"] } : {}),
139
+ ...(isStringList(o["ignores"]) ? { ignores: o["ignores"] } : {}),
110
140
  });
111
141
 
112
142
  const isRecord = (v: unknown): v is Record<string, unknown> =>
@@ -182,16 +212,15 @@ export function parseRuleBlocks(
182
212
  * `foreign` (a dependency's plugin, such as `@eslint/markdown`) are not paperlint's and are skipped.
183
213
  */
184
214
  export function shippedRuleIds(
185
- config: readonly unknown[],
215
+ config: readonly ConfigBlock[],
186
216
  foreign: readonly unknown[],
187
217
  ): Set<string> {
188
218
  const ids = new Set<string>();
189
219
  for (const block of config) {
190
- const plugins = (block as { plugins?: Record<string, unknown> }).plugins;
191
- for (const [name, plugin] of Object.entries(plugins ?? {})) {
220
+ for (const [name, plugin] of Object.entries(block.plugins ?? {})) {
192
221
  if (foreign.includes(plugin)) continue;
193
- const rules = (plugin as { rules?: Record<string, unknown> }).rules;
194
- for (const rule of Object.keys(rules ?? {})) ids.add(`${name}/${rule}`);
222
+ for (const rule of Object.keys(plugin.rules ?? {}))
223
+ ids.add(`${name}/${rule}`);
195
224
  }
196
225
  }
197
226
  return ids;
@@ -16,22 +16,12 @@ import {
16
16
  writeFileSync,
17
17
  } from "node:fs";
18
18
  import { tmpdir } from "node:os";
19
- import { dirname, join } from "node:path";
20
- import { fileURLToPath } from "node:url";
21
- import ts from "typescript";
22
- import { z } from "zod";
19
+ import { join } from "node:path";
23
20
  import { createChecker } from "../lib/check.ts";
24
21
 
25
- const HERE = dirname(fileURLToPath(import.meta.url));
26
- const VENUES = join(
27
- HERE,
28
- "..",
29
- "skills",
30
- "submit-paper",
31
- "references",
32
- "venues",
33
- );
34
22
  const R = await import("./tex-requirements.ts");
23
+ const { parseTemplate } = await import("./adapters/latex/index.ts");
24
+ const VENUES = (await import("./package-dirs.ts")).presetsDir();
35
25
 
36
26
  const check = createChecker();
37
27
  const throws = (fn: () => unknown): string => {
@@ -70,15 +60,15 @@ check(
70
60
  "cm-super" in base.packages,
71
61
  );
72
62
 
73
- // The whole profile, parsed the way the other harnesses read these files — never a line of text.
74
- const TemplateField = z.object({ template: z.unknown().optional() });
75
- const templateOf = (v: string): unknown =>
76
- TemplateField.parse(
77
- ts.parseConfigFileTextToJson(
78
- v,
79
- readFileSync(join(VENUES, `${v}.jsonc`), "utf8"),
80
- ).config,
81
- ).template;
63
+ // The class each profile's template names, as the parser reads it — never a line of text.
64
+ const presetOf = (v: string) =>
65
+ R.parsePreset(
66
+ readFileSync(join(VENUES, `${v}.jsonc`), "utf8"),
67
+ `${v}.jsonc`,
68
+ VENUES,
69
+ );
70
+ const templateOf = (v: string): string | undefined =>
71
+ parseTemplate(presetOf(v).template ?? "")?.cls;
82
72
  const acm = venues.filter((v) => templateOf(v) === "acmart");
83
73
  check(
84
74
  "at least one acmart venue is shipped (else the next checks see nothing)",
@@ -108,7 +98,7 @@ for (const v of acm) {
108
98
  // declares the template's packages, so this holds by construction; the check stays for any two
109
99
  // standalone presets that name the same template.
110
100
  const byTemplate = new Map<unknown, string[]>();
111
- for (const v of venues) {
101
+ for (const v of venues.filter((x) => presetOf(x).extends === null)) {
112
102
  const t = templateOf(v);
113
103
  byTemplate.set(t, [...(byTemplate.get(t) ?? []), v]);
114
104
  }
@@ -0,0 +1,21 @@
1
+ import assert from "node:assert/strict";
2
+ import { test } from "vitest";
3
+ import { loadTypescript } from "./tex-requirements.ts";
4
+
5
+ test("loadTypescript refuses a module without the two functions a profile is read with", () => {
6
+ assert.throws(
7
+ () => loadTypescript(() => ({})),
8
+ /loaded without parseConfigFileTextToJson/,
9
+ );
10
+ });
11
+
12
+ test("loadTypescript hands back what `require` loaded when it has them", () => {
13
+ const ts = {
14
+ parseConfigFileTextToJson: () => ({ config: {} }),
15
+ flattenDiagnosticMessageText: () => "",
16
+ };
17
+ assert.equal(
18
+ loadTypescript(() => ts),
19
+ ts,
20
+ );
21
+ });
@@ -16,8 +16,8 @@
16
16
  * `TexRequirements` and never looks at the text again; a malformed profile throws with the path
17
17
  * of every violation, it is never read as "no packages".
18
18
  *
19
- * Where the profiles live is answered by `packageVenuesDir()` in consumer.mjs — the one module
20
- * allowed to know where this package is installed (rule 10).
19
+ * Where the profiles live is answered by `presetsDir()` in `package-dirs.ts` — the one module
20
+ * allowed to know where this package's data is installed (rule 10).
21
21
  */
22
22
  // eslint-disable-next-line boundaries/dependencies -- legacy I/O, moves behind a port in #76
23
23
  import { readFileSync, readdirSync } from "node:fs";
@@ -25,8 +25,9 @@ import { createRequire } from "node:module";
25
25
  import { join } from "node:path";
26
26
  // eslint-disable-next-line boundaries/dependencies -- legacy layer, moves behind a port in #76
27
27
  import Ajv from "ajv";
28
- import { packageVenuesDir } from "../skills/paper-pipeline/scripts/consumer.mjs";
28
+ import { presetsDir } from "./package-dirs.ts";
29
29
  import { CONFIG_FILE } from "#lib/paper-config";
30
+ import { fieldOf } from "./domain/record.ts";
30
31
 
31
32
  /** CTAN package name → the names that prove it is installed. */
32
33
  export type PackageProofs = Readonly<Record<string, readonly string[]>>;
@@ -51,15 +52,37 @@ const PROFILE_EXT = ".jsonc";
51
52
 
52
53
  export const NO_REQUIREMENTS: TexRequirements = { packages: {}, tools: {} };
53
54
 
54
- type Ts = typeof import("typescript");
55
- let ts: Ts | null = null;
55
+ /** The two functions of `typescript` a profile is read with. */
56
+ type TsJsonc = Pick<
57
+ typeof import("typescript"),
58
+ "parseConfigFileTextToJson" | "flattenDiagnosticMessageText"
59
+ >;
60
+
61
+ const isTsJsonc = (m: unknown): m is TsJsonc =>
62
+ typeof fieldOf(m, "parseConfigFileTextToJson") === "function" &&
63
+ typeof fieldOf(m, "flattenDiagnosticMessageText") === "function";
64
+
65
+ /** `typescript` as `load` returns it — `require` answers `any` — checked for the two functions used. */
66
+ export function loadTypescript(load: (id: string) => unknown): TsJsonc {
67
+ const m = load("typescript");
68
+ if (!isTsJsonc(m))
69
+ throw new Error(
70
+ "`typescript` loaded without parseConfigFileTextToJson — reinstall this package",
71
+ );
72
+ return m;
73
+ }
74
+
75
+ let ts: TsJsonc | null = null;
56
76
  /** TypeScript is loaded only when a profile is read: `paperlint lint` never pays for it. */
57
- function typescript(): Ts {
58
- ts ??= createRequire(import.meta.url)("typescript") as Ts;
77
+ function typescript(): TsJsonc {
78
+ ts ??= loadTypescript(createRequire(import.meta.url));
59
79
  return ts;
60
80
  }
61
81
 
62
82
  type Validate = ReturnType<Ajv.Ajv["compile"]>;
83
+ /** `PresetJson` is the TypeScript side of the profile schema: a document the schema accepts is one. */
84
+ const matchesSchema = (v: unknown, validate: Validate): v is PresetJson =>
85
+ validate(v) === true;
63
86
  const validators = new Map<string, Validate>();
64
87
  function validatorFor(dir: string): Validate {
65
88
  const known = validators.get(dir);
@@ -125,6 +148,13 @@ export const NO_FORMAT: VenueFormat = {
125
148
  kinds: new Map(),
126
149
  };
127
150
 
151
+ /** A section a venue requires: its exact title, and whether it must close the paper. */
152
+ export interface RequiredSection {
153
+ readonly title: string;
154
+ /** "last": after every section of the body; null: anywhere. */
155
+ readonly position: "last" | null;
156
+ }
157
+
128
158
  /**
129
159
  * One preset FILE, parsed — before its `extends` chain is resolved (`src/presets.ts` does that).
130
160
  * A preset is a venue or a template family: `{ extends?, name?, template?, format?, tex?, rules? }`.
@@ -134,7 +164,12 @@ export interface PresetFile {
134
164
  readonly extends: string | null;
135
165
  /** A display name for messages; the file name otherwise. */
136
166
  readonly name: string | null;
167
+ /** The `\\documentclass` the venue's template uses, as the file spells it; null when it names none. */
137
168
  readonly template: string | null;
169
+ /** Other names the venue goes by in a paper's text; empty when the file names none. */
170
+ readonly aliases: readonly string[];
171
+ /** Sections the venue requires; null when the file names none (a child's list replaces its parent's). */
172
+ readonly requiredSections: readonly RequiredSection[] | null;
138
173
  /** Null when the file declares no `tex` block (allowed only with `extends`). */
139
174
  readonly tex: TexRequirements | null;
140
175
  readonly format: VenueFormat;
@@ -165,6 +200,11 @@ interface PresetJson {
165
200
  readonly extends?: string;
166
201
  readonly name?: string;
167
202
  readonly template?: string;
203
+ readonly aliases?: readonly string[];
204
+ readonly required_sections?: readonly {
205
+ readonly title: string;
206
+ readonly position?: "last";
207
+ }[];
168
208
  /** The schema requires `packages` in a `tex` block; `tools` is optional. */
169
209
  readonly tex?: Pick<TexRequirements, "packages"> &
170
210
  Partial<Pick<TexRequirements, "tools">>;
@@ -202,6 +242,14 @@ function formatOf(j: FormatJson = {}): VenueFormat {
202
242
  };
203
243
  }
204
244
 
245
+ /** A preset's `required_sections`, or null when it names none. */
246
+ const sectionsOf = (
247
+ r: PresetJson["required_sections"],
248
+ ): readonly RequiredSection[] | null =>
249
+ r === undefined
250
+ ? null
251
+ : r.map((x) => ({ title: x.title, position: orNull(x.position) }));
252
+
205
253
  /**
206
254
  * The text of one preset file → the typed file, or an Error naming every problem. The ONE parser
207
255
  * of a preset: the toolchain reads its `tex`, the venue rules its `format`, the config its `rules`.
@@ -213,7 +261,7 @@ function formatOf(j: FormatJson = {}): VenueFormat {
213
261
  export function parsePreset(
214
262
  text: string,
215
263
  file: string,
216
- dir: string = packageVenuesDir(),
264
+ dir: string = presetsDir(),
217
265
  ): PresetFile {
218
266
  const parsed = typescript().parseConfigFileTextToJson(file, text);
219
267
  const config: unknown = parsed.config;
@@ -223,15 +271,17 @@ export function parsePreset(
223
271
  `${file}: not valid JSONC — ${typescript().flattenDiagnosticMessageText(error.messageText, " ")}`,
224
272
  );
225
273
  const validate = validatorFor(dir);
226
- if (!validate(config))
274
+ if (!matchesSchema(config, validate))
227
275
  throw new Error(
228
276
  `${file} does not match ${SCHEMA_FILE}:\n ${violations(file, validate).join("\n ")}`,
229
277
  );
230
- const j = config as PresetJson;
278
+ const j = config;
231
279
  return {
232
280
  extends: orNull(j.extends),
233
281
  name: orNull(j.name),
234
282
  template: orNull(j.template),
283
+ aliases: j.aliases ?? [],
284
+ requiredSections: sectionsOf(j.required_sections),
235
285
  tex: j.tex ? { packages: j.tex.packages, tools: j.tex.tools ?? {} } : null,
236
286
  format: formatOf(j.format),
237
287
  rules: j.rules ?? {},
@@ -242,7 +292,7 @@ export function parsePreset(
242
292
  export function parseProfile(
243
293
  text: string,
244
294
  file: string,
245
- dir: string = packageVenuesDir(),
295
+ dir: string = presetsDir(),
246
296
  ): TexRequirements {
247
297
  return parsePreset(text, file, dir).tex ?? NO_REQUIREMENTS;
248
298
  }
@@ -264,7 +314,7 @@ function readProfile(dir: string, file: string): TexRequirements {
264
314
  }
265
315
 
266
316
  /** The venues that have a profile — every `.jsonc` in the directory except the base set. */
267
- export function venueNames(dir: string = packageVenuesDir()): string[] {
317
+ export function venueNames(dir: string = presetsDir()): string[] {
268
318
  return readdirSync(dir)
269
319
  .filter((f) => f.endsWith(PROFILE_EXT) && f !== BASE_PROFILE)
270
320
  .map((f) => f.slice(0, -PROFILE_EXT.length))
@@ -302,7 +352,7 @@ export function packageNames(tex: TexRequirements): string[] {
302
352
  */
303
353
  export function requirementsFor(
304
354
  preset: { readonly label: string; readonly tex: TexRequirements } | null,
305
- dir: string = packageVenuesDir(),
355
+ dir: string = presetsDir(),
306
356
  ): PaperRequirements {
307
357
  const base = readProfile(dir, BASE_PROFILE);
308
358
  return preset === null
@@ -322,7 +372,7 @@ export function requirementsFor(
322
372
  * builds any paper.
323
373
  */
324
374
  export function declaredUnion(
325
- dir: string = packageVenuesDir(),
375
+ dir: string = presetsDir(),
326
376
  extra: readonly TexRequirements[] = [],
327
377
  ): {
328
378
  readonly tex: TexRequirements;