paperlint 3.1.3 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (527) hide show
  1. package/.github/workflows/ci.yml +12 -7
  2. package/CLAUDE.md +5 -5
  3. package/CONTRIBUTING.md +34 -5
  4. package/dist/adapters/banal/failure.d.ts +1 -7
  5. package/dist/adapters/banal/failure.d.ts.map +1 -1
  6. package/dist/adapters/banal/failure.js +5 -7
  7. package/dist/adapters/banal/failure.js.map +1 -1
  8. package/dist/adapters/banal/index.d.ts.map +1 -1
  9. package/dist/adapters/banal/index.js +4 -3
  10. package/dist/adapters/banal/index.js.map +1 -1
  11. package/dist/adapters/banal/install.d.ts.map +1 -1
  12. package/dist/adapters/banal/install.js +4 -3
  13. package/dist/adapters/banal/install.js.map +1 -1
  14. package/dist/adapters/banal/invocation.d.ts +10 -5
  15. package/dist/adapters/banal/invocation.d.ts.map +1 -1
  16. package/dist/adapters/banal/invocation.js +13 -1
  17. package/dist/adapters/banal/invocation.js.map +1 -1
  18. package/dist/adapters/banal/locate.d.ts +12 -9
  19. package/dist/adapters/banal/locate.d.ts.map +1 -1
  20. package/dist/adapters/banal/locate.js +19 -12
  21. package/dist/adapters/banal/locate.js.map +1 -1
  22. package/dist/adapters/banal/run.d.ts +10 -1
  23. package/dist/adapters/banal/run.d.ts.map +1 -1
  24. package/dist/adapters/banal/run.js +8 -4
  25. package/dist/adapters/banal/run.js.map +1 -1
  26. package/dist/adapters/banal/settings.d.ts +1 -1
  27. package/dist/adapters/banal/settings.d.ts.map +1 -1
  28. package/dist/adapters/banal/settings.js +2 -1
  29. package/dist/adapters/banal/settings.js.map +1 -1
  30. package/dist/adapters/memory/index.d.ts.map +1 -1
  31. package/dist/adapters/memory/index.js +8 -2
  32. package/dist/adapters/memory/index.js.map +1 -1
  33. package/dist/adapters/node/files.io.d.ts.map +1 -1
  34. package/dist/adapters/node/files.io.js +2 -1
  35. package/dist/adapters/node/files.io.js.map +1 -1
  36. package/dist/adapters/node/process.io.d.ts +5 -7
  37. package/dist/adapters/node/process.io.d.ts.map +1 -1
  38. package/dist/adapters/node/process.io.js +4 -3
  39. package/dist/adapters/node/process.io.js.map +1 -1
  40. package/dist/adapters/node/workspace.io.d.ts.map +1 -1
  41. package/dist/adapters/node/workspace.io.js +4 -2
  42. package/dist/adapters/node/workspace.io.js.map +1 -1
  43. package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
  44. package/dist/adapters/pdfjs/fill.js +13 -7
  45. package/dist/adapters/pdfjs/fill.js.map +1 -1
  46. package/dist/adapters/references/index.d.ts.map +1 -1
  47. package/dist/adapters/references/index.js +8 -7
  48. package/dist/adapters/references/index.js.map +1 -1
  49. package/dist/adapters/references/pool.d.ts.map +1 -1
  50. package/dist/adapters/references/pool.js +4 -5
  51. package/dist/adapters/references/pool.js.map +1 -1
  52. package/dist/adapters/references/reach.io.d.ts.map +1 -1
  53. package/dist/adapters/references/reach.io.js +2 -1
  54. package/dist/adapters/references/reach.io.js.map +1 -1
  55. package/dist/build-engine.d.ts +1 -1
  56. package/dist/build-engine.d.ts.map +1 -1
  57. package/dist/build-engine.js +10 -3
  58. package/dist/build-engine.js.map +1 -1
  59. package/dist/build.d.ts +2 -3
  60. package/dist/build.d.ts.map +1 -1
  61. package/dist/build.js +19 -17
  62. package/dist/build.js.map +1 -1
  63. package/dist/caller-path.d.ts +3 -0
  64. package/dist/caller-path.d.ts.map +1 -0
  65. package/dist/caller-path.js +13 -0
  66. package/dist/caller-path.js.map +1 -0
  67. package/dist/cli.d.ts +10 -17
  68. package/dist/cli.d.ts.map +1 -1
  69. package/dist/cli.js +93 -46
  70. package/dist/cli.js.map +1 -1
  71. package/dist/doctor.d.ts +12 -3
  72. package/dist/doctor.d.ts.map +1 -1
  73. package/dist/doctor.js +5 -3
  74. package/dist/doctor.js.map +1 -1
  75. package/dist/domain/lookup-cache.d.ts.map +1 -1
  76. package/dist/domain/lookup-cache.js +10 -8
  77. package/dist/domain/lookup-cache.js.map +1 -1
  78. package/dist/domain/paths.d.ts +8 -2
  79. package/dist/domain/paths.d.ts.map +1 -1
  80. package/dist/domain/paths.js +12 -1
  81. package/dist/domain/paths.js.map +1 -1
  82. package/dist/domain/ready.d.ts +24 -0
  83. package/dist/domain/ready.d.ts.map +1 -0
  84. package/dist/domain/ready.js +11 -0
  85. package/dist/domain/ready.js.map +1 -0
  86. package/dist/domain/record.d.ts +8 -0
  87. package/dist/domain/record.d.ts.map +1 -0
  88. package/dist/domain/record.js +8 -0
  89. package/dist/domain/record.js.map +1 -0
  90. package/dist/domain/sha256.d.ts.map +1 -1
  91. package/dist/domain/sha256.js +4 -2
  92. package/dist/domain/sha256.js.map +1 -1
  93. package/dist/domain/text.d.ts +12 -0
  94. package/dist/domain/text.d.ts.map +1 -1
  95. package/dist/domain/text.js +17 -0
  96. package/dist/domain/text.js.map +1 -1
  97. package/dist/engine.d.ts +23 -22
  98. package/dist/engine.d.ts.map +1 -1
  99. package/dist/engine.js +4 -3
  100. package/dist/engine.js.map +1 -1
  101. package/dist/eslint-rules/bib-reachable-entry.d.ts +22 -0
  102. package/dist/eslint-rules/bib-reachable-entry.d.ts.map +1 -0
  103. package/dist/eslint-rules/bib-reachable-entry.js +75 -0
  104. package/dist/eslint-rules/bib-reachable-entry.js.map +1 -0
  105. package/dist/eslint-rules/install-path-literals.d.ts +80 -0
  106. package/dist/eslint-rules/install-path-literals.d.ts.map +1 -0
  107. package/dist/eslint-rules/install-path-literals.js +105 -0
  108. package/dist/eslint-rules/install-path-literals.js.map +1 -0
  109. package/dist/eslint-rules/latex-language.d.ts +136 -0
  110. package/dist/eslint-rules/latex-language.d.ts.map +1 -0
  111. package/dist/eslint-rules/latex-language.js +639 -0
  112. package/dist/eslint-rules/latex-language.js.map +1 -0
  113. package/dist/eslint-rules/paper-research-question.d.ts +31 -0
  114. package/dist/eslint-rules/paper-research-question.d.ts.map +1 -0
  115. package/dist/eslint-rules/paper-research-question.js +188 -0
  116. package/dist/eslint-rules/paper-research-question.js.map +1 -0
  117. package/dist/eslint-rules/paper-stages.d.ts +83 -0
  118. package/dist/eslint-rules/paper-stages.d.ts.map +1 -0
  119. package/dist/eslint-rules/paper-stages.js +358 -0
  120. package/dist/eslint-rules/paper-stages.js.map +1 -0
  121. package/dist/eslint-rules/paper-typography.d.ts +74 -0
  122. package/dist/eslint-rules/paper-typography.d.ts.map +1 -0
  123. package/dist/eslint-rules/paper-typography.js +404 -0
  124. package/dist/eslint-rules/paper-typography.js.map +1 -0
  125. package/dist/eslint-rules/papers.d.ts +42 -0
  126. package/dist/eslint-rules/papers.d.ts.map +1 -0
  127. package/dist/eslint-rules/papers.js +145 -0
  128. package/dist/eslint-rules/papers.js.map +1 -0
  129. package/dist/eslint-rules/pdf-last-page-balance.d.ts +72 -0
  130. package/dist/eslint-rules/pdf-last-page-balance.d.ts.map +1 -0
  131. package/dist/eslint-rules/pdf-last-page-balance.js +237 -0
  132. package/dist/eslint-rules/pdf-last-page-balance.js.map +1 -0
  133. package/dist/eslint-rules/review-frontmatter.d.ts +57 -0
  134. package/dist/eslint-rules/review-frontmatter.d.ts.map +1 -0
  135. package/dist/eslint-rules/review-frontmatter.js +126 -0
  136. package/dist/eslint-rules/review-frontmatter.js.map +1 -0
  137. package/dist/eslint-rules/review-frontmatter.schema.json +36 -0
  138. package/dist/eslint-rules/rule-context.d.ts +55 -0
  139. package/dist/eslint-rules/rule-context.d.ts.map +1 -0
  140. package/dist/eslint-rules/rule-context.js +9 -0
  141. package/dist/eslint-rules/rule-context.js.map +1 -0
  142. package/dist/eslint-rules/sibling-frontmatter.d.ts +25 -0
  143. package/dist/eslint-rules/sibling-frontmatter.d.ts.map +1 -0
  144. package/dist/eslint-rules/sibling-frontmatter.js +16 -0
  145. package/dist/eslint-rules/sibling-frontmatter.js.map +1 -0
  146. package/dist/eslint-rules/sibling-frontmatter.schema.json +14 -0
  147. package/dist/eslint-rules/temp-root-realpath.d.ts +71 -0
  148. package/dist/eslint-rules/temp-root-realpath.d.ts.map +1 -0
  149. package/dist/eslint-rules/temp-root-realpath.js +139 -0
  150. package/dist/eslint-rules/temp-root-realpath.js.map +1 -0
  151. package/dist/eslint-rules/tex-build.d.ts +35 -0
  152. package/dist/eslint-rules/tex-build.d.ts.map +1 -0
  153. package/dist/eslint-rules/tex-build.js +313 -0
  154. package/dist/eslint-rules/tex-build.js.map +1 -0
  155. package/dist/facts-file.d.ts.map +1 -1
  156. package/dist/facts-file.js +31 -27
  157. package/dist/facts-file.js.map +1 -1
  158. package/dist/hooks-settings.d.ts +1 -1
  159. package/dist/hooks-settings.d.ts.map +1 -1
  160. package/dist/hooks-settings.js +31 -11
  161. package/dist/hooks-settings.js.map +1 -1
  162. package/dist/init.d.ts +8 -9
  163. package/dist/init.d.ts.map +1 -1
  164. package/dist/init.js +33 -15
  165. package/dist/init.js.map +1 -1
  166. package/dist/latex-loop.d.ts +2 -2
  167. package/dist/latex-loop.d.ts.map +1 -1
  168. package/dist/latex-loop.js +2 -2
  169. package/dist/latex-loop.js.map +1 -1
  170. package/dist/lib/agent-cli-version.d.ts +65 -0
  171. package/dist/lib/agent-cli-version.d.ts.map +1 -0
  172. package/dist/lib/agent-cli-version.js +77 -0
  173. package/dist/lib/agent-cli-version.js.map +1 -0
  174. package/dist/lib/check.d.ts +19 -0
  175. package/dist/lib/check.d.ts.map +1 -0
  176. package/dist/lib/check.js +52 -0
  177. package/dist/lib/check.js.map +1 -0
  178. package/dist/lib/markdown.d.ts +180 -0
  179. package/dist/lib/markdown.d.ts.map +1 -0
  180. package/dist/lib/markdown.js +302 -0
  181. package/dist/lib/markdown.js.map +1 -0
  182. package/dist/lib/paper-config.d.ts +84 -0
  183. package/dist/lib/paper-config.d.ts.map +1 -0
  184. package/dist/lib/paper-config.js +138 -0
  185. package/dist/lib/paper-config.js.map +1 -0
  186. package/dist/lib/skill-eval-fixture.d.ts +29 -0
  187. package/dist/lib/skill-eval-fixture.d.ts.map +1 -0
  188. package/dist/lib/skill-eval-fixture.js +62 -0
  189. package/dist/lib/skill-eval-fixture.js.map +1 -0
  190. package/dist/link-skills.d.ts.map +1 -1
  191. package/dist/link-skills.js +5 -4
  192. package/dist/link-skills.js.map +1 -1
  193. package/dist/new-paper.d.ts.map +1 -1
  194. package/dist/new-paper.js +7 -3
  195. package/dist/new-paper.js.map +1 -1
  196. package/dist/paper-files.d.ts +4 -4
  197. package/dist/paper-files.d.ts.map +1 -1
  198. package/dist/paper-files.js +6 -6
  199. package/dist/paper-files.js.map +1 -1
  200. package/dist/paper-settings.d.ts.map +1 -1
  201. package/dist/paper-settings.js +8 -7
  202. package/dist/paper-settings.js.map +1 -1
  203. package/dist/pdf-facts.d.ts +51 -1
  204. package/dist/pdf-facts.d.ts.map +1 -1
  205. package/dist/pdf-facts.js +25 -16
  206. package/dist/pdf-facts.js.map +1 -1
  207. package/dist/pdf-geometry.d.ts.map +1 -1
  208. package/dist/pdf-geometry.js +3 -4
  209. package/dist/pdf-geometry.js.map +1 -1
  210. package/dist/ports/tool-installer.d.ts +2 -14
  211. package/dist/ports/tool-installer.d.ts.map +1 -1
  212. package/dist/presets.d.ts.map +1 -1
  213. package/dist/presets.js +19 -6
  214. package/dist/presets.js.map +1 -1
  215. package/dist/references.d.ts.map +1 -1
  216. package/dist/references.js +15 -10
  217. package/dist/references.js.map +1 -1
  218. package/dist/rules-config.d.ts +26 -1
  219. package/dist/rules-config.d.ts.map +1 -1
  220. package/dist/rules-config.js +15 -12
  221. package/dist/rules-config.js.map +1 -1
  222. package/dist/structure.js +1 -1
  223. package/dist/structure.js.map +1 -1
  224. package/dist/tex-requirements.d.ts +4 -0
  225. package/dist/tex-requirements.d.ts.map +1 -1
  226. package/dist/tex-requirements.js +22 -5
  227. package/dist/tex-requirements.js.map +1 -1
  228. package/dist/toolchain.d.ts.map +1 -1
  229. package/dist/toolchain.js +21 -16
  230. package/dist/toolchain.js.map +1 -1
  231. package/dist/types.d.ts +5 -3
  232. package/dist/types.d.ts.map +1 -1
  233. package/dist/venue-rules.d.ts.map +1 -1
  234. package/dist/venue-rules.js +21 -8
  235. package/dist/venue-rules.js.map +1 -1
  236. package/docs/configuration.md +2 -2
  237. package/docs/e2e.md +7 -7
  238. package/docs/install.md +1 -1
  239. package/docs/prior-art/README.md +1 -1
  240. package/docs/prior-art/package-location.md +5 -5
  241. package/docs/prior-art/repro/README.md +1 -1
  242. package/docs/prior-art/test-tooling.md +2 -2
  243. package/docs/testing.md +11 -8
  244. package/docs/toolchain.md +1 -1
  245. package/eslint-rules/{bib-reachable-entry.mjs → bib-reachable-entry.ts} +5 -4
  246. package/eslint-rules/install-path-literals.harness.mjs +1 -1
  247. package/eslint-rules/{install-path-literals.mjs → install-path-literals.ts} +18 -10
  248. package/eslint-rules/latex-language.harness.mjs +2 -2
  249. package/eslint-rules/latex-language.test.mjs +7 -1
  250. package/eslint-rules/{latex-language.mjs → latex-language.ts} +297 -109
  251. package/eslint-rules/paper-research-question.harness.mjs +3 -3
  252. package/eslint-rules/{paper-research-question.mjs → paper-research-question.ts} +35 -16
  253. package/eslint-rules/paper-stages.harness.mjs +2 -2
  254. package/eslint-rules/paper-stages.test.mjs +8 -2
  255. package/eslint-rules/{paper-stages.mjs → paper-stages.ts} +76 -31
  256. package/eslint-rules/paper-typography.test.mjs +3 -3
  257. package/eslint-rules/{paper-typography.mjs → paper-typography.ts} +134 -56
  258. package/eslint-rules/papers.harness.mjs +2 -2
  259. package/eslint-rules/{papers.mjs → papers.ts} +7 -4
  260. package/eslint-rules/pdf-last-page-balance.harness.mjs +3 -3
  261. package/eslint-rules/{pdf-last-page-balance.mjs → pdf-last-page-balance.ts} +89 -32
  262. package/eslint-rules/review-frontmatter.test.mjs +2 -2
  263. package/eslint-rules/{review-frontmatter.mjs → review-frontmatter.ts} +38 -16
  264. package/eslint-rules/rule-context.ts +62 -0
  265. package/eslint-rules/{sibling-frontmatter.mjs → sibling-frontmatter.ts} +1 -1
  266. package/eslint-rules/temp-root-realpath.harness.mjs +1 -1
  267. package/eslint-rules/{temp-root-realpath.mjs → temp-root-realpath.ts} +20 -7
  268. package/eslint-rules/tex-build.harness.mjs +2 -2
  269. package/eslint-rules/{tex-build.mjs → tex-build.ts} +6 -5
  270. package/eslint.config.mjs +146 -30
  271. package/fixtures/install-path-literals/clean.fixture.mjs +1 -1
  272. package/fixtures/install-path-literals/clean.md +2 -2
  273. package/fixtures/install-path-literals/defect.fixture.mjs +1 -1
  274. package/fixtures/install-path-literals/defect.md +2 -2
  275. package/fixtures/pdf-facts/README.md +2 -2
  276. package/fixtures/real-markdown-paper/{baseline.mjs → baseline.ts} +31 -10
  277. package/hooks/{hooks.harness.mjs → hooks.harness.ts} +51 -34
  278. package/hooks/paper-edit-guard.hook.mjs +1 -1
  279. package/hooks/{paper-edit-guard.test.mjs → paper-edit-guard.test.ts} +3 -7
  280. package/hooks/paper-skills-nudge.hook.mjs +1 -1
  281. package/hooks/{paper-skills-nudge.test.mjs → paper-skills-nudge.test.ts} +2 -6
  282. package/hooks/paper-status-gates.sh +9 -19
  283. package/hooks/{paper-status-gates.test.mjs → paper-status-gates.test.ts} +2 -6
  284. package/lib/agent-cli-version.harness.mjs +1 -1
  285. package/lib/{agent-cli-version.mjs → agent-cli-version.ts} +32 -18
  286. package/lib/check.test.ts +14 -9
  287. package/lib/{check.mjs → check.ts} +22 -9
  288. package/lib/markdown.test.mjs +7 -7
  289. package/lib/{markdown.mjs → markdown.ts} +68 -29
  290. package/lib/paper-config.harness.mjs +9 -9
  291. package/lib/{paper-config.mjs → paper-config.ts} +20 -19
  292. package/lib/skill-checks.mjs +24 -381
  293. package/lib/skill-checks.test.mjs +64 -116
  294. package/lib/skill-corpus.mjs +10 -251
  295. package/lib/skill-corpus.test.mjs +2 -13
  296. package/lib/skill-eval-kit.mjs +1 -1
  297. package/lib/skill-trigger-cases.mjs +1 -1
  298. package/package.json +42 -10
  299. package/scripts/{check.harness.mjs → check.harness.ts} +111 -10
  300. package/scripts/{check.test.mjs → check.test.ts} +13 -10
  301. package/scripts/{check.mjs → check.ts} +116 -42
  302. package/scripts/coverage-config.test.ts +80 -38
  303. package/scripts/eslint-report-guard.test.mjs +1 -1
  304. package/scripts/harness-api.frozen.json +14 -16
  305. package/scripts/harness-api.test.ts +8 -5
  306. package/scripts/layer-legacy-frozen.test.ts +17 -13
  307. package/scripts/{layer-legacy-frozen.mjs → layer-legacy-frozen.ts} +128 -39
  308. package/scripts/release-config.test.ts +12 -4
  309. package/scripts/rules-see-files.test.mjs +1 -1
  310. package/scripts/test.test.ts +78 -0
  311. package/scripts/test.ts +87 -0
  312. package/skills/analyze-sibling-paper/SKILL.md +1 -39
  313. package/skills/analyze-sibling-paper/SKILL.md.spec.ts +0 -38
  314. package/skills/argument-arc/SKILL.md +2 -36
  315. package/skills/argument-arc/SKILL.md.spec.ts +1 -43
  316. package/skills/build-benchmark/SKILL.md +1 -41
  317. package/skills/build-benchmark/SKILL.md.spec.ts +0 -40
  318. package/skills/camera-ready/SKILL.md +1 -34
  319. package/skills/camera-ready/SKILL.md.spec.ts +0 -33
  320. package/skills/cold-read-diff/SKILL.md +1 -35
  321. package/skills/cold-read-diff/SKILL.md.spec.ts +0 -34
  322. package/skills/draft-paper/SKILL.md +2 -41
  323. package/skills/draft-paper/SKILL.md.spec.ts +1 -50
  324. package/skills/extend-paper/SKILL.md +2 -33
  325. package/skills/extend-paper/SKILL.md.spec.ts +1 -42
  326. package/skills/find-venue/SKILL.md +2 -38
  327. package/skills/find-venue/SKILL.md.spec.ts +1 -47
  328. package/skills/grade-paper-writing/SKILL.md +3 -38
  329. package/skills/grade-paper-writing/SKILL.md.spec.ts +2 -47
  330. package/skills/grade-paper-writing/prose-lint.mjs +3 -4
  331. package/skills/grade-paper-writing/{prose-lint.test.mjs → prose-lint.test.ts} +3 -3
  332. package/skills/harden-paper/SKILL.md +1 -39
  333. package/skills/harden-paper/SKILL.md.spec.ts +0 -38
  334. package/skills/map-prior-work/SKILL.md +1 -35
  335. package/skills/map-prior-work/SKILL.md.spec.ts +0 -34
  336. package/skills/osf-artifact-upload/{osf-artifact-upload.harness.mjs → osf-artifact-upload.harness.ts} +3 -4
  337. package/skills/paper-adversarial-review/SKILL.md +2 -35
  338. package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -43
  339. package/skills/paper-pipeline/SKILL.md +2 -28
  340. package/skills/paper-pipeline/SKILL.md.spec.ts +1 -27
  341. package/skills/paper-pipeline/description-language.eval.mjs +1 -1
  342. package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +1 -1
  343. package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +2 -2
  344. package/skills/paper-pipeline/paper-pipeline.harness.mjs +25 -40
  345. package/skills/paper-pipeline/pipeline-firing.eval.mjs +1 -1
  346. package/skills/paper-pipeline/pipeline-language.eval.mjs +1 -1
  347. package/skills/paper-pipeline/references/acceptance-gate.md +1 -6
  348. package/skills/paper-pipeline/references/pipeline-status-template.md +8 -35
  349. package/skills/paper-pipeline/scripts/README.md +30 -225
  350. package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +4 -8
  351. package/skills/paper-pipeline/scripts/artifact-coverage.mjs +1 -1
  352. package/skills/paper-pipeline/scripts/{artifact-coverage.test.mjs → artifact-coverage.test.ts} +2 -2
  353. package/skills/paper-pipeline/scripts/consumer.harness.mjs +6 -104
  354. package/skills/paper-pipeline/scripts/consumer.mjs +24 -109
  355. package/skills/paper-pipeline/scripts/consumer.test.mjs +1 -1
  356. package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +1 -1
  357. package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +1 -4
  358. package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +1 -1
  359. package/skills/paper-pipeline/scripts/generated-code.harness.mjs +2 -3
  360. package/skills/paper-pipeline/scripts/{generated-code.test.mjs → generated-code.test.ts} +3 -3
  361. package/skills/paper-pipeline/scripts/population-map.harness.mjs +3 -3
  362. package/skills/paper-pipeline/scripts/population-map.mjs +1 -4
  363. package/skills/paper-pipeline/scripts/population-map.test.mjs +1 -1
  364. package/skills/paper-pipeline/scripts/{round-diff.harness.mjs → round-diff.harness.ts} +45 -18
  365. package/skills/paper-pipeline/scripts/round-diff.mjs +1 -1
  366. package/skills/paper-pipeline/scripts/round-diff.test.mjs +1 -1
  367. package/skills/paper-status/SKILL.md +2 -42
  368. package/skills/paper-status/SKILL.md.spec.ts +1 -41
  369. package/skills/pc-panel-review/SKILL.md +2 -46
  370. package/skills/pc-panel-review/SKILL.md.spec.ts +1 -45
  371. package/skills/plan-paper-timeline/SKILL.md +2 -36
  372. package/skills/plan-paper-timeline/SKILL.md.spec.ts +0 -36
  373. package/skills/plan-paper-timeline/fixtures/{fake-google-calendar.mjs → fake-google-calendar.ts} +101 -70
  374. package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +3 -3
  375. package/skills/render-paper/SKILL.md +1 -37
  376. package/skills/render-paper/SKILL.md.spec.ts +0 -36
  377. package/skills/render-paper/{extract-pdf-facts.harness.mjs → extract-pdf-facts.harness.ts} +34 -9
  378. package/skills/render-paper/render-paper.harness.mjs +2 -2
  379. package/skills/research-ideate/SKILL.md +2 -39
  380. package/skills/research-ideate/SKILL.md.spec.ts +1 -47
  381. package/skills/study-accepted-papers/SKILL.md +1 -34
  382. package/skills/study-accepted-papers/SKILL.md.spec.ts +0 -33
  383. package/skills/submit-paper/SKILL.md +1 -33
  384. package/skills/submit-paper/SKILL.md.spec.ts +0 -32
  385. package/skills/sweep-design-space/SKILL.md +1 -38
  386. package/skills/sweep-design-space/SKILL.md.spec.ts +0 -37
  387. package/skills/tighten-paper/SKILL.md +2 -39
  388. package/skills/tighten-paper/SKILL.md.spec.ts +1 -47
  389. package/skills/tighten-paper/structure.mjs +5 -12
  390. package/skills/tighten-paper/{structure.test.mjs → structure.test.ts} +4 -3
  391. package/skills/verify-citations/SKILL.md +2 -38
  392. package/skills/verify-citations/SKILL.md.spec.ts +1 -37
  393. package/skills/verify-citations/scripts/{bib-authors.cli.test.mjs → bib-authors.cli.test.ts} +14 -4
  394. package/skills/verify-citations/scripts/bib-authors.d.mts +63 -0
  395. package/skills/verify-citations/scripts/bib-authors.mjs +1 -4
  396. package/skills/verify-citations/scripts/{verify-cites.cli.test.mjs → verify-cites.cli.test.ts} +32 -9
  397. package/skills/verify-citations/scripts/verify-cites.d.mts +58 -0
  398. package/src/CLAUDE.md +1 -1
  399. package/src/adapters/banal/failure.ts +9 -10
  400. package/src/adapters/banal/index.test.ts +5 -8
  401. package/src/adapters/banal/index.ts +8 -7
  402. package/src/adapters/banal/install.ts +9 -4
  403. package/src/adapters/banal/invocation.test.ts +23 -9
  404. package/src/adapters/banal/invocation.ts +24 -9
  405. package/src/adapters/banal/locate.test.ts +2 -2
  406. package/src/adapters/banal/locate.ts +27 -21
  407. package/src/adapters/banal/output.test.ts +1 -1
  408. package/src/adapters/banal/run.test.ts +5 -5
  409. package/src/adapters/banal/run.ts +16 -10
  410. package/src/adapters/banal/settings.ts +2 -2
  411. package/src/adapters/banal/xml.test.ts +1 -1
  412. package/src/adapters/curl/download.test.ts +8 -6
  413. package/src/adapters/memory/index.ts +4 -3
  414. package/src/adapters/node/files.io.ts +2 -2
  415. package/src/adapters/node/files.test.ts +8 -4
  416. package/src/adapters/node/process.io.ts +13 -5
  417. package/src/adapters/node/process.test.ts +5 -5
  418. package/src/adapters/node/workspace.io.ts +9 -4
  419. package/src/adapters/node/workspace.test.ts +3 -1
  420. package/src/adapters/pdfjs/fill.test.ts +2 -2
  421. package/src/adapters/pdfjs/fill.ts +14 -7
  422. package/src/adapters/references/index.test.ts +31 -38
  423. package/src/adapters/references/index.ts +33 -60
  424. package/src/adapters/references/pool.ts +3 -5
  425. package/src/adapters/references/reach.io.ts +2 -1
  426. package/src/build-engine.harness.mjs +1 -1
  427. package/src/build-engine.test.ts +13 -8
  428. package/src/build-engine.ts +12 -7
  429. package/src/build.harness.mjs +2 -2
  430. package/src/build.test.ts +24 -24
  431. package/src/build.ts +34 -26
  432. package/src/caller-path.ts +13 -0
  433. package/src/cli.harness.mjs +6 -8
  434. package/src/cli.test.ts +31 -13
  435. package/src/cli.ts +117 -58
  436. package/src/doctor.harness.mjs +3 -3
  437. package/src/doctor.test.ts +1 -1
  438. package/src/doctor.ts +17 -7
  439. package/src/domain/lookup-cache.ts +21 -11
  440. package/src/domain/paths.test.ts +15 -0
  441. package/src/domain/paths.ts +20 -1
  442. package/src/domain/ready.ts +31 -0
  443. package/src/domain/record.test.ts +20 -0
  444. package/src/domain/record.ts +12 -0
  445. package/src/domain/result.test.ts +2 -2
  446. package/src/domain/sha256.ts +5 -3
  447. package/src/domain/text.test.ts +32 -0
  448. package/src/domain/text.ts +23 -0
  449. package/src/engine.harness.mjs +2 -2
  450. package/src/engine.ts +34 -5
  451. package/src/facts-file.test.ts +9 -7
  452. package/src/facts-file.ts +45 -29
  453. package/src/{hooks-settings.harness.mjs → hooks-settings.harness.ts} +56 -20
  454. package/src/hooks-settings.ts +48 -13
  455. package/src/init.test.ts +37 -14
  456. package/src/init.ts +48 -24
  457. package/src/{latex-log.harness.mjs → latex-log.harness.ts} +14 -14
  458. package/src/{latex-loop.harness.mjs → latex-loop.harness.ts} +64 -36
  459. package/src/latex-loop.ts +3 -3
  460. package/src/{link-skills.harness.mjs → link-skills.harness.ts} +19 -19
  461. package/src/link-skills.test.ts +3 -3
  462. package/src/link-skills.ts +5 -4
  463. package/src/lint-fix.test.ts +4 -6
  464. package/src/linted-set.test.ts +2 -2
  465. package/src/new-paper.harness.mjs +9 -25
  466. package/src/new-paper.test.ts +8 -10
  467. package/src/new-paper.ts +9 -5
  468. package/src/paper-files.test.ts +25 -24
  469. package/src/paper-files.ts +16 -27
  470. package/src/paper-settings-commands.test.ts +5 -4
  471. package/src/paper-settings.test.ts +7 -7
  472. package/src/paper-settings.ts +13 -14
  473. package/src/{pdf-facts.harness.mjs → pdf-facts.harness.ts} +9 -8
  474. package/src/pdf-facts.test.ts +25 -15
  475. package/src/pdf-facts.ts +81 -23
  476. package/src/{pdf-geometry.harness.mjs → pdf-geometry.harness.ts} +19 -12
  477. package/src/pdf-geometry.ts +8 -4
  478. package/src/ports/tool-installer.ts +2 -17
  479. package/src/presets.ts +31 -16
  480. package/src/references.test.ts +76 -62
  481. package/src/references.ts +18 -12
  482. package/src/{rules-config.harness.mjs → rules-config.harness.ts} +7 -13
  483. package/src/rules-config.ts +46 -14
  484. package/src/rules-off.test.ts +6 -4
  485. package/src/{structure.harness.mjs → structure.harness.ts} +26 -15
  486. package/src/structure.ts +1 -1
  487. package/src/{tex-requirements.harness.mjs → tex-requirements.harness.ts} +50 -22
  488. package/src/tex-requirements.test.ts +21 -0
  489. package/src/tex-requirements.ts +37 -11
  490. package/src/toolchain.harness.mjs +2 -2
  491. package/src/toolchain.test.ts +10 -6
  492. package/src/toolchain.ts +31 -17
  493. package/src/types.ts +5 -3
  494. package/src/venue-rules.test.ts +70 -68
  495. package/src/venue-rules.ts +22 -12
  496. package/tsconfig.json +6 -3
  497. package/tsconfig.pkg.json +10 -0
  498. package/lib/check.d.mts +0 -17
  499. package/lib/paper-config.d.mts +0 -31
  500. package/scripts/layer-legacy-frozen.d.mts +0 -36
  501. package/scripts/rules-are-content-only.harness.mjs +0 -105
  502. package/scripts/rules-are-content-only.mjs +0 -148
  503. package/scripts/rules-are-content-only.test.mjs +0 -61
  504. package/scripts/test.mjs +0 -44
  505. package/scripts/test.test.mjs +0 -52
  506. package/skills/paper-pipeline/scripts/announce.mjs +0 -67
  507. package/skills/paper-pipeline/scripts/announce.test.mjs +0 -102
  508. package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +0 -51
  509. package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +0 -52
  510. package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +0 -6
  511. package/skills/paper-pipeline/scripts/fixtures/scorecard.mjs +0 -71
  512. package/skills/paper-pipeline/scripts/ledger.cli.test.mjs +0 -302
  513. package/skills/paper-pipeline/scripts/ledger.mjs +0 -623
  514. package/skills/paper-pipeline/scripts/ledger.test.mjs +0 -254
  515. package/skills/paper-pipeline/scripts/pipeline-check.cli.test.mjs +0 -474
  516. package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +0 -328
  517. package/skills/paper-pipeline/scripts/pipeline-check.mjs +0 -753
  518. package/skills/paper-pipeline/scripts/pipeline-edges.mjs +0 -169
  519. package/skills/paper-pipeline/scripts/pipeline-edges.test.mjs +0 -73
  520. package/skills/paper-pipeline/scripts/run-mechanical.mjs +0 -648
  521. package/skills/paper-pipeline/scripts/run-mechanical.test.mjs +0 -468
  522. package/skills/paper-pipeline/scripts/status.mjs +0 -295
  523. package/skills/paper-pipeline/scripts/status.test.mjs +0 -232
  524. /package/bin/{paperlint.test.mjs → paperlint.test.ts} +0 -0
  525. /package/lib/{skill-eval-fixture.mjs → skill-eval-fixture.ts} +0 -0
  526. /package/skills/grade-paper-writing/__snapshots__/{prose-lint.test.mjs.snap → prose-lint.test.ts.snap} +0 -0
  527. /package/skills/tighten-paper/__snapshots__/{structure.test.mjs.snap → structure.test.ts.snap} +0 -0
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The one assertion helper every `*.harness.mjs` uses.
2
+ * The one assertion helper every harness uses.
3
3
  *
4
4
  * Until #52 each harness defined its own `check()`, and the copies had drifted: some printed a
5
5
  * detail on failure and some dropped it, some joined it with " — " and some with a newline, and two
@@ -17,15 +17,27 @@ import assert from "node:assert/strict";
17
17
  import { inspect } from "node:util";
18
18
  import { recordCheck } from "vigiles";
19
19
 
20
+ /** A failure detail: text, any value (rendered with `util.inspect`), or a thunk producing one. */
21
+ export type Detail = unknown;
22
+
23
+ /** `check(label, cond, detail?)`, and how many times it has been called. */
24
+ export interface Check {
25
+ (label: string, cond: unknown, detail?: Detail): void;
26
+ readonly count: number;
27
+ }
28
+
29
+ /** A detail given as a function, so it is only built on failure. */
30
+ const isThunk = (d: Detail): d is () => unknown => typeof d === "function";
31
+
20
32
  /** A failure detail as text: strings verbatim, other values through `util.inspect`, a thunk called. */
21
- export function renderDetail(detail) {
22
- const value = typeof detail === "function" ? detail() : detail;
33
+ export function renderDetail(detail: Detail): string {
34
+ const value: unknown = isThunk(detail) ? detail() : detail;
23
35
  if (value === undefined || value === null || value === "") return "";
24
36
  return typeof value === "string" ? value : inspect(value, { depth: 6 });
25
37
  }
26
38
 
27
39
  /** The label, then the detail: on the same line when it is one line, below it when it is many. */
28
- export function failureMessage(label, detail) {
40
+ export function failureMessage(label: string, detail: Detail): string {
29
41
  const shown = renderDetail(detail);
30
42
  if (!shown) return label;
31
43
  return shown.includes("\n") ? `${label}\n${shown}` : `${label} — ${shown}`;
@@ -35,16 +47,17 @@ export function failureMessage(label, detail) {
35
47
  * A fresh `check(label, cond, detail?)`. `check.count` is how many times it has been called.
36
48
  * `log` is where a failure is printed — stderr unless this module's own test injects one.
37
49
  */
38
- export function createChecker({ log = console.error } = {}) {
39
- let count = 0;
40
- const check = (label, cond, detail) => {
41
- count++;
50
+ export function createChecker({
51
+ log = console.error,
52
+ }: { log?: (line: string) => void } = {}): Check {
53
+ const check = (label: string, cond: unknown, detail?: Detail): void => {
54
+ check.count++;
42
55
  recordCheck();
43
56
  if (cond) return;
44
57
  const message = failureMessage(label, detail);
45
58
  log(`✗ ${message}`);
46
59
  assert.fail(message);
47
60
  };
48
- Object.defineProperty(check, "count", { get: () => count });
61
+ check.count = 0;
49
62
  return check;
50
63
  }
@@ -10,7 +10,7 @@ import { copyFileSync, mkdirSync } from "node:fs";
10
10
  import { dirname, join } from "node:path";
11
11
  import { fileURLToPath } from "node:url";
12
12
  import { test } from "vitest";
13
- import { runNode, useTempDir, writeTree } from "../test/support.mjs";
13
+ import { runNode, useTempDir, writeTree } from "../test/support.ts";
14
14
  import {
15
15
  blankFrontmatter,
16
16
  fences,
@@ -22,7 +22,7 @@ import {
22
22
  stripFences,
23
23
  stripFrontmatter,
24
24
  tables,
25
- } from "./markdown.mjs";
25
+ } from "./markdown.ts";
26
26
 
27
27
  const DOC = [
28
28
  "---", // 0
@@ -157,13 +157,13 @@ writeTree(root, {
157
157
  "import { register } from 'node:module';\n" +
158
158
  "register('data:text/javascript,' + encodeURIComponent(\"export async function resolve(s, c, next) { if (s === 'markdown-it') throw Object.assign(new Error('gone'), { code: 'ERR_MODULE_NOT_FOUND' }); return next(s, c); }\"));\n",
159
159
  "probe.mjs":
160
- `const m = await import(${JSON.stringify(join(dirname(fileURLToPath(import.meta.url)), "markdown.mjs"))});\n` +
160
+ `const m = await import(${JSON.stringify(join(dirname(fileURLToPath(import.meta.url)), "markdown.ts"))});\n` +
161
161
  "const t = '# H\\n\\n```\\nx\\n```\\n';\n" +
162
162
  "console.log(JSON.stringify([m.MD_AVAILABLE(), m.headings(t), m.fences(t), m.stripFences(t), m.paragraphs(t), m.tables(t)]));\n" +
163
163
  "m.requireMarkdown();\n" +
164
164
  "console.log('not reached');\n",
165
165
  "probe-plugin.mjs":
166
- `const m = await import(${JSON.stringify(join(dirname(fileURLToPath(import.meta.url)), "markdown.mjs"))});\n` +
166
+ `const m = await import(${JSON.stringify(join(dirname(fileURLToPath(import.meta.url)), "markdown.ts"))});\n` +
167
167
  'm.requireMarkdown({ from: "file:///home/u/.claude/plugins/cache/paperlint/lib/markdown.mjs" });\n',
168
168
  });
169
169
 
@@ -223,12 +223,12 @@ test("a copy running from a plugin cache without markdown-it is told to install
223
223
  const lib = join(root, ".claude", "plugins", "paperlint", "lib");
224
224
  mkdirSync(lib, { recursive: true });
225
225
  copyFileSync(
226
- join(dirname(fileURLToPath(import.meta.url)), "markdown.mjs"),
227
- join(lib, "markdown.mjs"),
226
+ join(dirname(fileURLToPath(import.meta.url)), "markdown.ts"),
227
+ join(lib, "markdown.ts"),
228
228
  );
229
229
  writeTree(lib, {
230
230
  "probe.mjs":
231
- 'const m = await import("./markdown.mjs");\nm.requireMarkdown();\n',
231
+ 'const m = await import("./markdown.ts");\nm.requireMarkdown();\n',
232
232
  });
233
233
  const r = runNode(join(lib, "probe.mjs"), []);
234
234
  assert.deepEqual(
@@ -32,7 +32,28 @@
32
32
  // a bare top-level `import` changed the shape of the failure: no `node_modules` → exit 1
33
33
  // → **the blocking `pre` gate in paper-lint stops blocking silently**, because
34
34
  // PreToolUse counts only exit code 2 as a block. Measured: `ERR_MODULE_NOT_FOUND`, exit 1.
35
- let md = null;
35
+ import type MarkdownIt from "markdown-it";
36
+ import type { Token } from "markdown-it";
37
+
38
+ /** One ATX heading: its level, its text without the hashes, its 0-based line and line offset. */
39
+ export interface Heading {
40
+ readonly depth: number;
41
+ readonly text: string;
42
+ readonly line: number;
43
+ readonly offset: number;
44
+ }
45
+
46
+ /** A chunk of the document: the heading it starts with (`null` for the preamble), with and without it. */
47
+ export interface Section {
48
+ readonly heading: Heading | null;
49
+ readonly raw: string;
50
+ readonly body: string;
51
+ }
52
+
53
+ /** A block tokenizer with markdown-it's shape — the default, or one a test passes. */
54
+ export type Parse = (text: string) => readonly Token[];
55
+
56
+ let md: MarkdownIt | null = null;
36
57
  try {
37
58
  const { default: MarkdownIt } = await import("markdown-it");
38
59
  md = new MarkdownIt();
@@ -50,7 +71,10 @@ try {
50
71
  export const MD_AVAILABLE = () => md !== null;
51
72
 
52
73
  /** markdown-it's block tokens for `text`, or null when it did not resolve. A test passes its own. */
53
- const defaultParse = () => (md === null ? null : (text) => md.parse(text, {}));
74
+ const defaultParse = (): Parse | null => {
75
+ const parser = md;
76
+ return parser === null ? null : (text) => parser.parse(text, {});
77
+ };
54
78
 
55
79
  /**
56
80
  * The frontmatter is BLANKED with spaces, not cut out: the offsets must stay the
@@ -60,7 +84,7 @@ const defaultParse = () => (md === null ? null : (text) => md.parse(text, {}));
60
84
  * sections.
61
85
  */
62
86
  const FRONTMATTER_RE = /^---[ \t]*\r?\n[\s\S]*?\r?\n---[ \t]*(?=\r?\n|$)/;
63
- export function blankFrontmatter(text) {
87
+ export function blankFrontmatter(text: string): string {
64
88
  const m = FRONTMATTER_RE.exec(text);
65
89
  return m ? m[0].replace(/[^\n]/g, " ") + text.slice(m[0].length) : text;
66
90
  }
@@ -78,7 +102,7 @@ export function blankFrontmatter(text) {
78
102
  * and rejected: gray-matter"). The value here is one fence instead of eight, and the
79
103
  * parsing of VALUES is done by `js-yaml` at the caller.
80
104
  */
81
- export function frontmatterBlock(text) {
105
+ export function frontmatterBlock(text: string): string | null {
82
106
  const m = FRONTMATTER_RE.exec(text);
83
107
  if (!m) return null;
84
108
  const inner = m[0]
@@ -96,13 +120,13 @@ export function frontmatterBlock(text) {
96
120
  * later shown to the user; cutting — where the text is merely counted. Choose by what
97
121
  * the caller does, not by taste.
98
122
  */
99
- export function stripFrontmatter(text) {
123
+ export function stripFrontmatter(text: string): string {
100
124
  const m = FRONTMATTER_RE.exec(text);
101
125
  return m ? text.slice(m[0].length).replace(/^\r?\n/, "") : text;
102
126
  }
103
127
 
104
128
  /** Line-start offsets — so that `token.map` becomes a position in the source string. */
105
- function lineStarts(text) {
129
+ function lineStarts(text: string): number[] {
106
130
  const out = [0];
107
131
  for (let i = 0; i < text.length; i++) if (text[i] === "\n") out.push(i + 1);
108
132
  return out;
@@ -121,13 +145,15 @@ function lineStarts(text) {
121
145
  * under without a blank line would become a level-2 heading per CommonMark. The former
122
146
  * regexps knew only ATX, so this preserves the contract rather than narrowing it.
123
147
  */
124
- export function headings(text, { parse = defaultParse() } = {}) {
148
+ export function headings(
149
+ text: string,
150
+ { parse = defaultParse() }: { parse?: Parse | null } = {},
151
+ ): Heading[] {
125
152
  if (parse === null) return [];
126
153
  const toks = parse(blankFrontmatter(text));
127
154
  const starts = lineStarts(text);
128
- const out = [];
129
- for (let i = 0; i < toks.length; i++) {
130
- const t = toks[i];
155
+ const out: Heading[] = [];
156
+ for (const [i, t] of toks.entries()) {
131
157
  if (t.type !== "heading_open" || !t.map) continue;
132
158
  if (!t.markup || !t.markup.startsWith("#")) continue; // setext — see the caveat above
133
159
  // markdown-it follows `heading_open` with its `inline` token and maps blocks to lines of the
@@ -166,18 +192,22 @@ export function headings(text, { parse = defaultParse() } = {}) {
166
192
  * happened (`recordBlock` in `skill-corpus.mjs` returned a single "#" character for all
167
193
  * 21 skills).
168
194
  */
169
- export function splitSections(text, { min = 2, max = 6 } = {}) {
195
+ export function splitSections(
196
+ text: string,
197
+ { min = 2, max = 6 }: { min?: number; max?: number } = {},
198
+ ): Section[] {
170
199
  const hs = headings(text).filter((h) => h.depth >= min && h.depth <= max);
171
- const out = [];
172
- const preEnd = hs.length ? hs[0].offset : text.length;
200
+ const out: Section[] = [];
201
+ const [first] = hs;
202
+ const preEnd = first ? first.offset : text.length;
173
203
  out.push({
174
204
  heading: null,
175
205
  raw: text.slice(0, preEnd),
176
206
  body: text.slice(0, preEnd),
177
207
  });
178
- for (let i = 0; i < hs.length; i++) {
179
- const h = hs[i];
180
- const end = i + 1 < hs.length ? hs[i + 1].offset : text.length;
208
+ for (const [i, h] of hs.entries()) {
209
+ const next = hs[i + 1];
210
+ const end = next ? next.offset : text.length;
181
211
  const nl = text.indexOf("\n", h.offset);
182
212
  const bodyStart = nl === -1 || nl + 1 > end ? end : nl + 1;
183
213
  out.push({
@@ -221,18 +251,21 @@ export function splitSections(text, { min = 2, max = 6 } = {}) {
221
251
  * degradation is acceptable (see the module comment), while a silent fallback to a
222
252
  * regex would mean a working degraded version that nobody notices.
223
253
  */
224
- export function fences(text) {
254
+ export function fences(text: string): string[] {
225
255
  if (md === null) return [];
226
- const out = [];
256
+ const out: string[] = [];
227
257
  for (const t of md.parse(blankFrontmatter(text), {}))
228
258
  if (t.type === "fence" || t.type === "code_block") out.push(t.content);
229
259
  return out;
230
260
  }
231
261
 
232
- export function stripFences(text, { blank = false } = {}) {
262
+ export function stripFences(
263
+ text: string,
264
+ { blank = false }: { blank?: boolean } = {},
265
+ ): string {
233
266
  if (md === null) return text;
234
267
  const lines = text.split("\n");
235
- const drop = new Set();
268
+ const drop = new Set<number>();
236
269
  for (const t of md.parse(blankFrontmatter(text), {}))
237
270
  if ((t.type === "fence" || t.type === "code_block") && t.map)
238
271
  for (let i = t.map[0]; i < t.map[1]; i++) drop.add(i);
@@ -249,7 +282,9 @@ export function stripFences(text, { blank = false } = {}) {
249
282
  * indistinguishable from "the paper has no sections". A confident zero has already been
250
283
  * read as a finding four times in this repo; here it is inexpressible.
251
284
  */
252
- export function requireMarkdown({ from = import.meta.url } = {}) {
285
+ export function requireMarkdown({
286
+ from = import.meta.url,
287
+ }: { from?: string } = {}): void {
253
288
  if (MD_AVAILABLE()) return;
254
289
  // 🔴 THE CURE DEPENDS ON WHERE THIS COPY RUNS FROM. Measured 2026-09-19: a Claude Code plugin
255
290
  // copy is installed without dependencies (`npm pack` strips `package-lock.json`, and the plugin
@@ -301,23 +336,27 @@ export function requireMarkdown({ from = import.meta.url } = {}) {
301
336
  * automatically, by the construction of the parser, and not by a separate line-type
302
337
  * check.
303
338
  */
304
- export function paragraphs(text) {
339
+ export function paragraphs(text: string): { line: number; endLine: number }[] {
305
340
  if (md === null) return [];
306
341
  return md
307
342
  .parse(blankFrontmatter(text), {})
308
- .filter((t) => t.type === "paragraph_open" && t.map)
309
- .map((t) => ({ line: t.map[0], endLine: t.map[1] }));
343
+ .flatMap((t) =>
344
+ t.type === "paragraph_open" && t.map
345
+ ? [{ line: t.map[0], endLine: t.map[1] }]
346
+ : [],
347
+ );
310
348
  }
311
349
 
312
- export function tables(text) {
350
+ export function tables(
351
+ text: string,
352
+ ): { line: number; endLine: number; rows: string[][] }[] {
313
353
  if (md === null) return [];
314
354
  const lines = blankFrontmatter(text).split("\n");
315
- const out = [];
355
+ const out: { line: number; endLine: number; rows: string[][] }[] = [];
316
356
  for (const t of md.parse(blankFrontmatter(text), {})) {
317
357
  if (t.type !== "table_open" || !t.map) continue;
318
- const rows = [];
319
- for (let i = t.map[0]; i < t.map[1]; i++) {
320
- const raw = lines[i];
358
+ const rows: string[][] = [];
359
+ for (const raw of lines.slice(t.map[0], t.map[1])) {
321
360
  if (/^\s*\|?[\s:|-]+\|?\s*$/.test(raw) && raw.includes("-")) continue; // alignment
322
361
  rows.push(
323
362
  raw
@@ -2,7 +2,7 @@
2
2
  * Both halves for `lib/paper-config.mjs` — and, more broadly, for the SET of places that
3
3
  * publish these two constants.
4
4
  *
5
- * 🔴 WHY THIS CHECK MOVED HERE FROM `hooks/hooks.harness.mjs`. There it compared three hooks
5
+ * 🔴 WHY THIS CHECK MOVED HERE FROM `hooks/hooks.harness.ts`. There it compared three hooks
6
6
  * against each other — and was right only as far as its COVERAGE was right. Measured
7
7
  * 2026-09-18: there were eight declarations across five files, four per constant, and for each
8
8
  * constant exactly one copy sat OUTSIDE the comparison (`eslint-rules/papers.mjs`,
@@ -27,7 +27,7 @@
27
27
  import { readFileSync, readdirSync } from "node:fs";
28
28
  import { dirname, join, relative, resolve } from "node:path";
29
29
  import { fileURLToPath, pathToFileURL } from "node:url";
30
- import { createChecker } from "./check.mjs";
30
+ import { createChecker } from "./check.ts";
31
31
 
32
32
  const HERE = dirname(fileURLToPath(import.meta.url));
33
33
  const ROOT = resolve(HERE, "..");
@@ -37,15 +37,15 @@ const check = createChecker();
37
37
  /**
38
38
  * Every place that publishes at least one of the two constants. The three hooks duplicate them
39
39
  * out of necessity (a compiled hook is forbidden to import anything but `vigiles/hook`); the
40
- * rest re-export from `lib/paper-config.mjs` and are kept in this list for exactly one reason:
40
+ * rest re-export from `lib/paper-config.ts` and are kept in this list for exactly one reason:
41
41
  * so a re-export that is one day swapped back for its own declaration gets caught.
42
42
  */
43
43
  const CARRIERS = [
44
- "lib/paper-config.mjs",
44
+ "lib/paper-config.ts",
45
45
  "hooks/paper-edit-guard.hook.mjs",
46
46
  "hooks/paper-skills-nudge.hook.mjs",
47
47
  "hooks/paper-status-gates.hook.mjs",
48
- "eslint-rules/papers.mjs",
48
+ "eslint-rules/papers.ts",
49
49
  "skills/paper-pipeline/scripts/consumer.mjs",
50
50
  ];
51
51
 
@@ -167,7 +167,7 @@ const loaded = await Promise.all(
167
167
  "repro",
168
168
  ]);
169
169
  const declares =
170
- /^(?:export )?const (?:CONFIG_KEY|CONFIG_FILE|DEFAULT_PAPERS_ROOT|PAPERS_DIR_FIELD)\s*=/m;
170
+ /^(?:export )?const (?:CONFIG_KEY|CONFIG_FILE|DEFAULT_PAPERS_ROOT|PAPERS_DIR_FIELD)\s*(?::[^=]*)?=/m;
171
171
  const found = [];
172
172
  const walk = (dir) => {
173
173
  for (const e of readdirSync(dir, { withFileTypes: true })) {
@@ -202,7 +202,7 @@ const loaded = await Promise.all(
202
202
  // every `settingsOf(root)?.<key>` (and the older `[CONFIG_KEY]?.<key>` shape), plus every field of
203
203
  // `PaperlintConfig`, the CLI's typed view of the same object.
204
204
  {
205
- const { SETTINGS_KEYS } = await import(join(HERE, "paper-config.mjs"));
205
+ const { SETTINGS_KEYS } = await import(join(HERE, "paper-config.ts"));
206
206
  const ts = (await import("typescript")).default;
207
207
  const readKeys = new Set();
208
208
  const isConfigKey = (e) =>
@@ -253,7 +253,7 @@ const loaded = await Promise.all(
253
253
  );
254
254
  check(
255
255
  "and the scan found the readers it must find — the skill scripts' keys and PaperlintConfig's",
256
- ["ledger", "citeChecks", "triggerCases", "papersDir", "rules"].every((k) =>
256
+ ["scripts", "timezone", "triggerCases", "papersDir", "rules"].every((k) =>
257
257
  readKeys.has(k),
258
258
  ),
259
259
  );
@@ -262,7 +262,7 @@ const loaded = await Promise.all(
262
262
  // ── the two readers' fallbacks: no marker anywhere up the tree, and JSON that is not an object ──
263
263
  {
264
264
  const { findProjectRoot, settingsOf } = await import(
265
- join(HERE, "paper-config.mjs")
265
+ join(HERE, "paper-config.ts")
266
266
  );
267
267
  check(
268
268
  "findProjectRoot: no paperlint.json and no package.json up to / — the start directory itself",
@@ -9,7 +9,7 @@
9
9
  * allowing a reach into `child_process`. The restriction is wider than strictly necessary
10
10
  * (importing plain constants adds no capability), and that is discussed in zernie/vigiles#245,
11
11
  * but until it is relaxed the hooks duplicate, and drift is caught by the cross-check in
12
- * `hooks/hooks.harness.mjs`.
12
+ * `hooks/hooks.harness.ts`.
13
13
  *
14
14
  * 🔴 BUT THE REST WERE DUPLICATING FOR NO REASON AT ALL. `eslint-rules/papers.mjs` and
15
15
  * `skills/paper-pipeline/scripts/consumer.mjs` are not hooks, carry no import restriction, and
@@ -28,7 +28,7 @@ import { dirname, join, resolve } from "node:path";
28
28
  * and the hooks resolve their scripts through it. It equals the package name, and
29
29
  * `lib/paper-config.harness.mjs` checks that.
30
30
  */
31
- export const CONFIG_KEY = "paperlint";
31
+ export const CONFIG_KEY: string = "paperlint";
32
32
 
33
33
  /**
34
34
  * THE SETTINGS FILE, ONE NAME AT TWO LEVELS, ONE SCHEMA (`SETTINGS_KEYS`):
@@ -41,13 +41,13 @@ export const CONFIG_KEY = "paperlint";
41
41
  * applied after the root's. A venue preset (`paperlint:<name>`, or the project's own `./x.jsonc`)
42
42
  * is the third level, below both.
43
43
  */
44
- export const CONFIG_FILE = "paperlint.json";
44
+ export const CONFIG_FILE: string = "paperlint.json";
45
45
 
46
46
  /**
47
47
  * The files that make a directory a PAPER. A `paperlint.json` beside one of them is that paper's,
48
48
  * never the project's — so walking up from inside a paper does not stop at the paper's own file.
49
49
  */
50
- export const PAPER_MARKERS = Object.freeze([
50
+ export const PAPER_MARKERS: readonly string[] = Object.freeze([
51
51
  "paper.tex",
52
52
  "paper.md",
53
53
  "draft.md",
@@ -61,13 +61,16 @@ export const PAPER_MARKERS = Object.freeze([
61
61
  *
62
62
  * @param isFile whether a path exists — `existsSync` by default; `src/` passes its Files port.
63
63
  */
64
- export function findProjectRoot(startDir, isFile = existsSync) {
64
+ export function findProjectRoot(
65
+ startDir: string,
66
+ isFile: (path: string) => boolean = existsSync,
67
+ ): string {
65
68
  const start = resolve(startDir);
66
- const isPaper = (d) => PAPER_MARKERS.some((m) => isFile(join(d, m)));
69
+ const isPaper = (d: string) => PAPER_MARKERS.some((m) => isFile(join(d, m)));
67
70
  // Up from `start` to the first directory `found` accepts; null past the filesystem root. A
68
71
  // loop with an exit condition rather than `for (;;)`: the code after an endless loop is a block
69
72
  // that can never run, and coverage counts it as one that did not.
70
- const walk = (found) => {
73
+ const walk = (found: (dir: string) => boolean): string | null => {
71
74
  let d = start;
72
75
  while (!found(d)) {
73
76
  if (dirname(d) === d) return null;
@@ -82,7 +85,7 @@ export function findProjectRoot(startDir, isFile = existsSync) {
82
85
  );
83
86
  }
84
87
 
85
- const isObject = (v) =>
88
+ const isObject = (v: unknown): v is Readonly<Record<string, unknown>> =>
86
89
  v !== null && typeof v === "object" && !Array.isArray(v);
87
90
 
88
91
  /**
@@ -90,8 +93,10 @@ const isObject = (v) =>
90
93
  * such file, or it does not parse, or it is not an object. For readers that only need one key and
91
94
  * fall back to its default; `paperlint lint` parses the file strictly and names what is wrong.
92
95
  */
93
- export function settingsOf(root) {
94
- let v;
96
+ export function settingsOf(
97
+ root: string,
98
+ ): Readonly<Record<string, unknown>> | undefined {
99
+ let v: unknown;
95
100
  try {
96
101
  v = JSON.parse(readFileSync(join(root, CONFIG_FILE), "utf8"));
97
102
  } catch {
@@ -101,7 +106,7 @@ export function settingsOf(root) {
101
106
  }
102
107
 
103
108
  /** The default papers directory, used when the root `paperlint.json` declares none (or there is none). */
104
- export const DEFAULT_PAPERS_ROOT = "papers";
109
+ export const DEFAULT_PAPERS_ROOT: string = "papers";
105
110
 
106
111
  /**
107
112
  * The field that names the papers directory: `{ "papersDir": "papers" }` in the root
@@ -112,7 +117,7 @@ export const DEFAULT_PAPERS_ROOT = "papers";
112
117
  * here. The three hooks cannot import this module, so they keep their own copy, and
113
118
  * `lib/paper-config.harness.mjs` checks that every copy matches this one.
114
119
  */
115
- export const PAPERS_DIR_FIELD = "papersDir";
120
+ export const PAPERS_DIR_FIELD: string = "papersDir";
116
121
 
117
122
  /**
118
123
  * Every key a `paperlint.json` may hold — the root's and a paper's, ONE schema — with who reads it.
@@ -124,7 +129,7 @@ export const PAPERS_DIR_FIELD = "papersDir";
124
129
  * `PaperlintConfig`, and requires each to be here — so a new reader that forgets this list turns
125
130
  * the harness red before a consumer's config does.
126
131
  */
127
- export const SETTINGS_KEYS = Object.freeze({
132
+ export const SETTINGS_KEYS: Readonly<Record<string, string>> = Object.freeze({
128
133
  papersDir: `the papers directory, default "papers" — every command, the hooks`,
129
134
  structure: "which files a paper directory must hold — paperlint lint",
130
135
  rules:
@@ -133,24 +138,20 @@ export const SETTINGS_KEYS = Object.freeze({
133
138
  "the venue preset: paperlint:<name> or ./path.jsonc — the pdf/ venue rules, paperlint build",
134
139
  kind: "the kind of paper, whose page limit applies — pdf/limits",
135
140
  pdf: "where the built PDF is, relative to the paper, when it is not paper.pdf — the facts",
136
- ledger: "the run ledger — skills/paper-pipeline/scripts/consumer.mjs",
137
141
  scripts: "how skill prose names the scripts — consumer.mjs",
138
- timezone: "dates in the ledger — consumer.mjs",
142
+ timezone: "the zone deadline anchors are written in — consumer.mjs",
139
143
  contactEmail: "the polite-pool address for citation lookups — consumer.mjs",
140
- citeChecks: "the consumer's citation checkers — run-mechanical.mjs",
141
144
  triggerCases:
142
145
  "the consumer's own skill trigger cases — lib/skill-trigger-cases.mjs",
143
146
  $comment: "a note for humans (JSON Schema's own comment keyword); ignored",
144
147
  });
145
148
 
146
149
  /** The keys that only mean something for the whole project: refused in a paper's `paperlint.json`. */
147
- export const ROOT_ONLY_KEYS = Object.freeze([
150
+ export const ROOT_ONLY_KEYS: readonly string[] = Object.freeze([
148
151
  "papersDir",
149
152
  "structure",
150
- "ledger",
151
153
  "scripts",
152
154
  "timezone",
153
155
  "contactEmail",
154
- "citeChecks",
155
156
  "triggerCases",
156
157
  ]);