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,6 +1,6 @@
1
1
  # PDF fixtures for the pdf.js reader
2
2
 
3
- Real PDFs that `src/pdf-facts.harness.mjs` and `skills/render-paper/extract-pdf-facts.harness.mjs`
3
+ Real PDFs that `src/pdf-facts.harness.ts` and `skills/render-paper/extract-pdf-facts.harness.ts`
4
4
  read. They were measured with poppler and with pdf.js on 2026-09-24 (issue #61); the harnesses pin
5
5
  poppler's numbers, so a pass means agreement with the tool pdf.js replaced.
6
6
 
@@ -13,7 +13,7 @@ poppler's numbers, so a pass means agreement with the tool pdf.js replaced.
13
13
  | `encrypted.pdf` | `t3-all.pdf` saved with an AES-256 user password (PyMuPDF) | pdf.js refuses it with `PasswordException`; the reader reports `encrypted` |
14
14
  | `hidden-text.pdf` | `hidden-text.tex`, pdflatex | two-column body text beside near-white, rotated and render-mode-3 text; page 2 holds only hidden text, so banal calls it blank |
15
15
 
16
- `test/e2e/banal.mjs` also reads these files, with the real banal: it pins what banal 1.2 printed for
16
+ `test/e2e/banal.ts` also reads these files, with the real banal: it pins what banal 1.2 printed for
17
17
  each one on poppler's `pdftohtml` 24.02.0 (2026-09-25), so a pass there means paperlint's pdf.js-written
18
18
  XML measures the same as poppler's. `hidden-text.pdf` exists for that test: each kind of hidden text
19
19
  changes banal's answer if it is counted.
@@ -1,18 +1,31 @@
1
1
  /**
2
2
  * ONE reader of `baseline.json`, shared by the two runs that compare against it: the harness
3
- * beside this file (the repository's own `bin/paperlint.mjs`) and `test/e2e/install.mjs` (the binary
3
+ * beside this file (the repository's own `bin/paperlint.mjs`) and `test/e2e/install.ts` (the binary
4
4
  * a consumer actually got). Two copies of "growth fails, a drop never does" would drift the first
5
5
  * time one of them is tightened, and the two runs would then disagree about the same article.
6
6
  */
7
7
  import { readFileSync } from "node:fs";
8
8
  import { dirname, join } from "node:path";
9
9
  import { fileURLToPath } from "node:url";
10
+ import { z } from "zod";
11
+
12
+ /** Findings per rule id. */
13
+ export type Counts = Record<string, number>;
14
+
15
+ const Baseline = z.object({ findings: z.record(z.string(), z.number()) });
16
+ const LintOutput = z.array(
17
+ z.object({
18
+ messages: z.array(z.object({ ruleId: z.string().nullable() })).nullish(),
19
+ }),
20
+ );
10
21
 
11
22
  const HERE = dirname(fileURLToPath(import.meta.url));
12
23
 
13
24
  /** The recorded counts, `{ruleId: n}`. */
14
- export function recordedFindings() {
15
- return JSON.parse(readFileSync(join(HERE, "baseline.json"), "utf8")).findings;
25
+ export function recordedFindings(): Counts {
26
+ return Baseline.parse(
27
+ JSON.parse(readFileSync(join(HERE, "baseline.json"), "utf8")),
28
+ ).findings;
16
29
  }
17
30
 
18
31
  /**
@@ -20,11 +33,13 @@ export function recordedFindings() {
20
33
  * 🔴 Output that does not parse THROWS. An empty set read as "clean" is how this repository's
21
34
  * checks have gone hollow before.
22
35
  */
23
- export function countByRule(stdout) {
24
- const out = {};
25
- for (const file of JSON.parse(stdout))
26
- for (const m of file.messages ?? [])
27
- out[m.ruleId] = (out[m.ruleId] ?? 0) + 1;
36
+ export function countByRule(stdout: string): Counts {
37
+ const out: Counts = {};
38
+ for (const file of LintOutput.parse(JSON.parse(stdout)))
39
+ for (const m of file.messages ?? []) {
40
+ const rule = String(m.ruleId);
41
+ out[rule] = (out[rule] ?? 0) + 1;
42
+ }
28
43
  return out;
29
44
  }
30
45
 
@@ -37,12 +52,18 @@ export function countByRule(stdout) {
37
52
  * A partial drop is neither: it is what a fix looks like (paperlint#44 is expected to take typography
38
53
  * to zero, and then the recording is updated in the same change).
39
54
  */
40
- export function compareToBaseline(found, recorded = recordedFindings()) {
55
+ export function compareToBaseline(
56
+ found: Readonly<Counts>,
57
+ recorded: Readonly<Counts> = recordedFindings(),
58
+ ): {
59
+ grew: { rule: string; now: number; recorded: number }[];
60
+ vanished: string[];
61
+ } {
41
62
  const grew = Object.entries(found)
42
63
  .filter(([rule, n]) => n > (recorded[rule] ?? 0))
43
64
  .map(([rule, n]) => ({ rule, now: n, recorded: recorded[rule] ?? 0 }));
44
65
  const vanished = Object.keys(recorded).filter(
45
- (r) => recorded[r] > 0 && !(r in found),
66
+ (r) => (recorded[r] ?? 0) > 0 && !(r in found),
46
67
  );
47
68
  return { grew, vanished };
48
69
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * hooks.harness.mjs — the three shipped hooks, each asserted in BOTH halves.
2
+ * hooks.harness.ts — the three shipped hooks, each asserted in BOTH halves.
3
3
  *
4
4
  * 🔴 WHY "BOTH HALVES" IS THE ORGANISING RULE HERE AND NOT A STYLE. Two of these three are
5
5
  * ADVISORY: silence is their success state. So "it printed nothing" and "it is dead" look
@@ -20,9 +20,9 @@
20
20
  * `checkHookImports`, over the shipped files directly. That is STRICTER than compiling a twin
21
21
  * `.ts` source would be: a twin can drift from its build, and there is no twin here to drift.
22
22
  *
23
- * Run: `npx vigiles test hooks/hooks.harness.mjs`
23
+ * Run: `npx vigiles test hooks/hooks.harness.ts`
24
24
  */
25
- import { runHook } from "vigiles";
25
+ import { runHook, type HookInput, type HookRunResult } from "vigiles";
26
26
  import { checkHookImports } from "vigiles/hook";
27
27
  import {
28
28
  mkdtempSync,
@@ -37,8 +37,9 @@ import {
37
37
  import { tmpdir } from "node:os";
38
38
  import { dirname, join, resolve } from "node:path";
39
39
  import { fileURLToPath } from "node:url";
40
- import { PAPERS_DIR_FIELD } from "../lib/paper-config.mjs";
41
- import { createChecker } from "../lib/check.mjs";
40
+ import { z } from "zod";
41
+ import { PAPERS_DIR_FIELD } from "../lib/paper-config.ts";
42
+ import { createChecker } from "../lib/check.ts";
42
43
 
43
44
  const HOOKS = dirname(fileURLToPath(import.meta.url));
44
45
  const ROOT = resolve(HOOKS, "..");
@@ -64,7 +65,13 @@ const check = createChecker();
64
65
  * installed rather than adjacent. The `node_modules/<pkg>` entry is a symlink to this very
65
66
  * checkout, which is what `npm install` of a local package does anyway.
66
67
  */
67
- const consumer = (block, { papers = "docs/papers", paper = "alpha" } = {}) => {
68
+ const consumer = (
69
+ block: unknown,
70
+ {
71
+ papers = "docs/papers",
72
+ paper = "alpha",
73
+ }: { papers?: string | null; paper?: string } = {},
74
+ ) => {
68
75
  const dir = realpathSync(mkdtempSync(join(tmpdir(), "paperlint-hooks-")));
69
76
  const nm = join(dir, "node_modules");
70
77
  mkdirSync(nm, { recursive: true });
@@ -84,24 +91,28 @@ const consumer = (block, { papers = "docs/papers", paper = "alpha" } = {}) => {
84
91
  };
85
92
 
86
93
  /** `run-program <hook>` exactly as a consumer's settings block invokes it. */
87
- const program = (name) =>
94
+ const program = (name: string) =>
88
95
  `node ${JSON.stringify(CLI)} hook-runtime run-program ${JSON.stringify(join(HOOKS, name + ".hook.mjs"))}`;
89
96
 
90
- const onBash = (command) => ({
97
+ const onBash = (command: string): HookInput => ({
91
98
  hook_event_name: "PreToolUse",
92
99
  tool_name: "Bash",
93
100
  tool_input: { command },
94
101
  });
95
- const onEdit = (file_path) => ({
102
+ const onEdit = (file_path: string): HookInput => ({
96
103
  hook_event_name: "PostToolUse",
97
104
  tool_name: "Edit",
98
105
  tool_input: { file_path },
99
106
  tool_response: {},
100
107
  });
101
- const STOP = { hook_event_name: "Stop", tool_name: "", tool_input: {} };
108
+ const STOP: HookInput = {
109
+ hook_event_name: "Stop",
110
+ tool_name: "",
111
+ tool_input: {},
112
+ };
102
113
 
103
114
  /** Run a hook AS a consumer: cwd and `CLAUDE_PROJECT_DIR` both point at the fixture repo. */
104
- const at = (dir, name, input) =>
115
+ const at = (dir: string, name: string, input: HookInput): HookRunResult =>
105
116
  runHook(program(name), input, { cwd: dir, env: { CLAUDE_PROJECT_DIR: dir } });
106
117
 
107
118
  /**
@@ -116,23 +127,29 @@ const at = (dir, name, input) =>
116
127
  * live case: a session `cd`s into a subtree, and vigiles runs every provider «via execSync in
117
128
  * the hook's cwd».
118
129
  */
119
- const adrift = (dir, name, input) =>
130
+ const adrift = (dir: string, name: string, input: HookInput): HookRunResult =>
120
131
  runHook(program(name), input, {
121
132
  cwd: realpathSync(mkdtempSync(join(tmpdir(), "drift-"))),
122
133
  env: { CLAUDE_PROJECT_DIR: dir },
123
134
  });
124
135
 
125
136
  /** An injected notice, as the model would receive it — not the returned reaction. */
126
- const injected = (r) => {
137
+ const Injected = z.object({
138
+ hookSpecificOutput: z.object({ additionalContext: z.string().nullish() }),
139
+ });
140
+ const injected = (r: HookRunResult): string => {
127
141
  try {
128
- return JSON.parse(r.stdout).hookSpecificOutput.additionalContext ?? "";
142
+ return (
143
+ Injected.parse(JSON.parse(r.stdout)).hookSpecificOutput
144
+ .additionalContext ?? ""
145
+ );
129
146
  } catch {
130
147
  return "";
131
148
  }
132
149
  };
133
150
 
134
- const tmps = [];
135
- const fixture = (...args) => {
151
+ const tmps: string[] = [];
152
+ const fixture = (...args: Parameters<typeof consumer>) => {
136
153
  const d = consumer(...args);
137
154
  tmps.push(d);
138
155
  return d;
@@ -191,10 +208,10 @@ try {
191
208
  const dir = fixture({ [PAPERS_DIR_FIELD]: "docs/papers" });
192
209
  const P = "docs/papers/alpha/paper.md";
193
210
  const T = "docs/papers/alpha/paper.tex";
194
- const deny = (label, cmd) => {
211
+ const deny = (label: string, cmd: string) => {
195
212
  const r = at(dir, "paper-edit-guard", onBash(cmd));
196
213
  check(
197
- `guard BLOCKS ${label} (rc=${r.exitCode})`,
214
+ `guard BLOCKS ${label} (rc=${String(r.exitCode)})`,
198
215
  r.exitCode === 2 && r.blocked,
199
216
  );
200
217
  check(
@@ -202,10 +219,10 @@ try {
202
219
  /Use Edit or Write/.test(r.stderr),
203
220
  );
204
221
  };
205
- const allow = (label, cmd) => {
222
+ const allow = (label: string, cmd: string) => {
206
223
  const r = at(dir, "paper-edit-guard", onBash(cmd));
207
224
  check(
208
- `guard ALLOWS ${label} (rc=${r.exitCode}, ${r.stderr.length}b err)`,
225
+ `guard ALLOWS ${label} (rc=${String(r.exitCode)}, ${String(r.stderr.length)}b err)`,
209
226
  r.exitCode === 0 && !r.blocked,
210
227
  );
211
228
  };
@@ -292,7 +309,7 @@ try {
292
309
  check("drift · guard still blocks a paper write", write.exitCode === 2);
293
310
  const idle = adrift(dir, "paper-edit-guard", onBash("echo hi"));
294
311
  check(
295
- `drift · guard does NOT deny an unrelated command (rc=${idle.exitCode})`,
312
+ `drift · guard does NOT deny an unrelated command (rc=${String(idle.exitCode)})`,
296
313
  idle.exitCode === 0,
297
314
  );
298
315
  const guarded = adrift(
@@ -318,7 +335,7 @@ try {
318
335
  onEdit(`${dir}/writing/drafts/alpha/PIPELINE-STATUS.md`),
319
336
  );
320
337
  check(
321
- `drift · the status-gates hook still runs (rc=${gates.exitCode})`,
338
+ `drift · the status-gates hook still runs (rc=${String(gates.exitCode)})`,
322
339
  gates.exitCode === 0,
323
340
  );
324
341
  }
@@ -381,7 +398,7 @@ try {
381
398
  const dir = fixture({ [PAPERS_DIR_FIELD]: null });
382
399
  const r = at(dir, "paper-edit-guard", harmless);
383
400
  check(
384
- `carrier: "${PAPERS_DIR_FIELD}": null REFUSES (rc=${r.exitCode})`,
401
+ `carrier: "${PAPERS_DIR_FIELD}": null REFUSES (rc=${String(r.exitCode)})`,
385
402
  r.exitCode === 2,
386
403
  );
387
404
  check(
@@ -394,7 +411,7 @@ try {
394
411
  const dir = fixture({ [PAPERS_DIR_FIELD]: "" });
395
412
  const r = at(dir, "paper-edit-guard", harmless);
396
413
  check(
397
- `carrier: "${PAPERS_DIR_FIELD}": "" REFUSES (rc=${r.exitCode})`,
414
+ `carrier: "${PAPERS_DIR_FIELD}": "" REFUSES (rc=${String(r.exitCode)})`,
398
415
  r.exitCode === 2,
399
416
  );
400
417
  check(
@@ -414,7 +431,7 @@ try {
414
431
  );
415
432
  const r = at(dir, "paper-edit-guard", harmless);
416
433
  check(
417
- `carrier: an unparseable paperlint.json REFUSES (rc=${r.exitCode})`,
434
+ `carrier: an unparseable paperlint.json REFUSES (rc=${String(r.exitCode)})`,
418
435
  r.exitCode === 2,
419
436
  );
420
437
  check(
@@ -442,11 +459,11 @@ try {
442
459
  // ═══════════════════════════════════════════════════════════════════════════
443
460
  {
444
461
  const dir = fixture({ [PAPERS_DIR_FIELD]: "docs/papers" });
445
- const lands = (label, p) => {
462
+ const lands = (label: string, p: string) => {
446
463
  const r = at(dir, "paper-skills-nudge", onEdit(p));
447
464
  const ctx = injected(r);
448
465
  check(
449
- `nudge LANDS on ${label} (${r.stdout.length}b stdout)`,
466
+ `nudge LANDS on ${label} (${String(r.stdout.length)}b stdout)`,
450
467
  ctx.length > 0,
451
468
  );
452
469
  check(
@@ -455,10 +472,10 @@ try {
455
472
  );
456
473
  check(`nudge on ${label} exits 0`, r.exitCode === 0);
457
474
  };
458
- const silent = (label, p) => {
475
+ const silent = (label: string, p: string) => {
459
476
  const r = at(dir, "paper-skills-nudge", onEdit(p));
460
477
  check(
461
- `nudge SILENT on ${label} (${r.stdout.length}b stdout, ${r.stderr.length}b stderr)`,
478
+ `nudge SILENT on ${label} (${String(r.stdout.length)}b stdout, ${String(r.stderr.length)}b stderr)`,
462
479
  r.stdout.length === 0 && r.stderr.length === 0 && r.exitCode === 0,
463
480
  );
464
481
  };
@@ -505,15 +522,15 @@ try {
505
522
  // ═══════════════════════════════════════════════════════════════════════════
506
523
  {
507
524
  const dir = fixture({ [PAPERS_DIR_FIELD]: "docs/papers" });
508
- const fires = (label, p) => {
525
+ const fires = (label: string, p: string) => {
509
526
  const r = at(dir, "paper-status-gates", onEdit(p));
510
527
  check(
511
- `gates FIRES on ${label} (${r.stderr.length}b stderr)`,
528
+ `gates FIRES on ${label} (${String(r.stderr.length)}b stderr)`,
512
529
  /fixture verdict line/.test(r.stderr),
513
530
  );
514
531
  check(`gates on ${label} exits 0`, r.exitCode === 0);
515
532
  };
516
- const silent = (label, p) => {
533
+ const silent = (label: string, p: string) => {
517
534
  const r = at(dir, "paper-status-gates", onEdit(p));
518
535
  check(
519
536
  `gates SILENT on ${label}`,
@@ -593,7 +610,7 @@ try {
593
610
  // ═══════════════════════════════════════════════════════════════════════════
594
611
  {
595
612
  const dir = fixture({ [PAPERS_DIR_FIELD]: "docs/papers" });
596
- const sh = (args) =>
613
+ const sh = (args: string) =>
597
614
  runHook(
598
615
  `bash ${JSON.stringify(join(HOOKS, "paper-status-gates.sh"))} ${args}`,
599
616
  STOP,
@@ -643,7 +660,7 @@ try {
643
660
  );
644
661
  }
645
662
 
646
- console.log(`hooks.harness: ${check.count} assertions passed`);
663
+ console.log(`hooks.harness: ${String(check.count)} assertions passed`);
647
664
  } finally {
648
665
  for (const d of tmps) rmSync(d, { recursive: true, force: true });
649
666
  }
@@ -38,7 +38,7 @@
38
38
  * install reverting it.
39
39
  *
40
40
  * The capability surface is therefore asserted HERE, on the artifact that actually executes:
41
- * `hooks.harness.mjs` runs `checkHookImports` over every shipped `.hook.mjs`. That is a stricter
41
+ * `hooks.harness.ts` runs `checkHookImports` over every shipped `.hook.mjs`. That is a stricter
42
42
  * check than compiling a twin `.ts` source would be, because a twin can drift from its build and
43
43
  * this cannot — there is only one file.
44
44
  *
@@ -7,12 +7,8 @@
7
7
  */
8
8
  import assert from "node:assert/strict";
9
9
  import { test } from "vitest";
10
- import {
11
- hookConsumer,
12
- onBash,
13
- runShippedHook,
14
- } from "../test/hook-consumer.mjs";
15
- import { useTempDir } from "../test/support.mjs";
10
+ import { hookConsumer, onBash, runShippedHook } from "../test/hook-consumer.ts";
11
+ import { useTempDir } from "../test/support.ts";
16
12
 
17
13
  const dir = hookConsumer(useTempDir("paper-edit-guard-"), {
18
14
  "papers/alpha/PIPELINE-STATUS.md": "# S\n",
@@ -22,7 +18,7 @@ const GT = String.fromCharCode(62);
22
18
  const P = "papers/alpha/paper.md";
23
19
 
24
20
  /** The guard's exit code for one Bash command: 2 denies, 0 lets it through. */
25
- const guard = (command) =>
21
+ const guard = (command: string): number =>
26
22
  runShippedHook("paper-edit-guard", onBash(command), dir).exitCode;
27
23
 
28
24
  test("a `>` inside quotes, after an escaped quote, or escaped itself is not a redirection", () => {
@@ -28,7 +28,7 @@
28
28
  * load at all, and a consumer-side thin spec that imports this logic is refused by
29
29
  * `vigiles compile` and can therefore never be re-stamped. The full reasoning, with both
30
30
  * measurements, is in that file's header; the capability surface of this one is asserted by
31
- * `hooks.harness.mjs` running `checkHookImports` over the shipped artifact.
31
+ * `hooks.harness.ts` running `checkHookImports` over the shipped artifact.
32
32
  */
33
33
  import {
34
34
  experimental_defineReact,
@@ -5,12 +5,8 @@
5
5
  import assert from "node:assert/strict";
6
6
  import { join } from "node:path";
7
7
  import { test } from "vitest";
8
- import {
9
- hookConsumer,
10
- onEdit,
11
- runShippedHook,
12
- } from "../test/hook-consumer.mjs";
13
- import { useTempDir } from "../test/support.mjs";
8
+ import { hookConsumer, onEdit, runShippedHook } from "../test/hook-consumer.ts";
9
+ import { useTempDir } from "../test/support.ts";
14
10
 
15
11
  const root = useTempDir("paper-skills-nudge-");
16
12
 
@@ -2,7 +2,7 @@
2
2
  # paper-status-gates.sh — NOT A HOOK. This is a TOOL invoked by `paper-status-gates.hook.mjs`,
3
3
  # which owns the event routing and decides WHICH paper (if any) an edit concerned.
4
4
  #
5
- # --surface <paper-dir> print the readiness verdict + run the pipeline checker
5
+ # --surface <paper-dir> print the readiness verdict and the unrun gates
6
6
  #
7
7
  # 🔴 WHAT LEFT THIS FILE AND WHY IT MATTERS. It used to read its payload with
8
8
  # `INPUT="${CLAUDE_TOOL_INPUT:-}"` plus a `timeout 1 cat` fallback and then grep that blob for a
@@ -28,22 +28,21 @@ case "$pdir_name" in
28
28
  esac
29
29
 
30
30
  # ── WHERE THE CONSUMER'S THINGS ARE — READ, NOT ASSUMED ─────────────────────
31
- # Both paths come from the one declaration every other carrier of this package reads, the
32
- # project's root paperlint.json (optional — absent means every default):
31
+ # The papers path comes from the one declaration every other carrier of this package reads, the
32
+ # project's root paperlint.json (optional — absent means the default):
33
33
  #
34
- # { "papersDir": "docs/papers", "scripts": "tools/pipeline" }
34
+ # { "papersDir": "docs/papers" }
35
35
  #
36
36
  # ⚠️ `node -p` RATHER THAN grep/sed ON THE FILE. A JSON value is not a line of text: it can
37
37
  # be quoted, escaped, or spread across lines, and a pattern that gets it right today gets it
38
38
  # wrong on the first reformat — silently, by producing a root that matches nothing. Node is
39
- # already a hard dependency of the checker this script runs, so there is no new requirement.
39
+ # already a hard dependency of the hook that runs this script, so there is no new requirement.
40
40
  ROOT_DIR="${CLAUDE_PROJECT_DIR:-.}"
41
41
  read_key() { # $1 = key, $2 = default
42
42
  node -p "((c) => c['$1'] ?? '$2')((() => { try { return require('$ROOT_DIR/paperlint.json') } catch { return {} } })())" \
43
43
  2>/dev/null || echo "$2"
44
44
  }
45
45
  papers_root="$(read_key papersDir papers)"
46
- scripts_root="$(read_key scripts .claude/skills/paper-pipeline/scripts)"
47
46
 
48
47
  # 🔴 AN UNUSABLE ROOT EXITS, IT DOES NOT BUILD A PATH FROM IT. `node -p` prints `undefined` for a
49
48
  # key whose value is literally `null` (`?? ` only catches null/undefined AFTER the object lookup,
@@ -69,18 +68,9 @@ else
69
68
  echo " whether this paper is submittable, which is the one question it exists to answer." >&2
70
69
  fi
71
70
 
72
- CHECK="$ROOT_DIR/$scripts_root/pipeline-check.mjs"
73
- if [ -f "$CHECK" ]; then
74
- # The real checker: stale continuous passes, gates missing their declared inputs, a study that
75
- # ran before the claim was stated, submit-access inside the moderation window, a verdict that is
76
- # only a percentage, unrun gates. Advisory, always exit 0.
77
- node "$CHECK" "$ROOT_DIR/$pdir" >&2 || true
78
- else
79
- # Fallback for a checkout without the skill tree — the old crude surface.
80
- gates=$(grep -E '☐' "$STATUS" 2>/dev/null | grep -iE 'study-accepted|pc-panel|harden|verify.?citations' || true)
81
- if [ -n "$gates" ]; then
82
- echo "⚠️ UNRUN GATES in $pdir/PIPELINE-STATUS.md (☐ — run them, don't 'anti-churn'-skip):" >&2
83
- echo "$gates" | sed -E 's/\|.*//; s/^\| */ /' >&2
84
- fi
71
+ gates=$(grep -E '☐' "$STATUS" 2>/dev/null | grep -iE 'study-accepted|pc-panel|harden|verify.?citations' || true)
72
+ if [ -n "$gates" ]; then
73
+ echo "⚠️ UNRUN GATES in $pdir/PIPELINE-STATUS.md (☐ — run them, don't 'anti-churn'-skip):" >&2
74
+ echo "$gates" | sed -E 's/\|.*//; s/^\| */ /' >&2
85
75
  fi
86
76
  exit 0
@@ -5,12 +5,8 @@
5
5
  import assert from "node:assert/strict";
6
6
  import { join } from "node:path";
7
7
  import { test } from "vitest";
8
- import {
9
- hookConsumer,
10
- onEdit,
11
- runShippedHook,
12
- } from "../test/hook-consumer.mjs";
13
- import { useTempDir } from "../test/support.mjs";
8
+ import { hookConsumer, onEdit, runShippedHook } from "../test/hook-consumer.ts";
9
+ import { useTempDir } from "../test/support.ts";
14
10
 
15
11
  const root = useTempDir("paper-status-gates-");
16
12
  const STATUS = "**Readiness verdict:** ✅ fixture verdict line\n";
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import assert from "node:assert/strict";
10
10
  import { recordCheck } from "vigiles";
11
- import { observeAgentCli, CHARACTERIZED_CLI } from "./agent-cli-version.mjs";
11
+ import { observeAgentCli, CHARACTERIZED_CLI } from "./agent-cli-version.ts";
12
12
 
13
13
  /** A spawner that records how it was called, so the CALL SHAPE is assertable, not just the result. */
14
14
  const spawner = (result) => {
@@ -39,28 +39,42 @@ export const CHARACTERIZED_CLI = "2.1.227";
39
39
  /** What `claude --version` prints: `2.1.273 (Claude Code)`. */
40
40
  const VERSION_IN = /\b(\d+\.\d+\.\d+)\b/;
41
41
 
42
- /**
43
- * @typedef {object} CliObservation
44
- * @property {boolean} present the binary answered `--version` with status 0
45
- * @property {string} raw the full line it printed, trimmed
46
- * @property {string} version the dotted version parsed out of it
47
- * @property {string} characterized the version the assertions were written against
48
- * @property {boolean} drifted observed and characterized differ
49
- * @property {string} note one line naming BOTH numbers, for the report and for failures
50
- */
42
+ /** What one `--version` probe of the agent CLI observed. */
43
+ export interface CliObservation {
44
+ /** The binary answered `--version` with status 0. */
45
+ present: boolean;
46
+ /** The full line it printed, trimmed. */
47
+ raw: string;
48
+ /** The dotted version parsed out of it. */
49
+ version: string;
50
+ /** The version the assertions were written against. */
51
+ characterized: string;
52
+ /** Observed and characterized differ. */
53
+ drifted: boolean;
54
+ /** One line naming BOTH numbers, for the report and for failures. */
55
+ note: string;
56
+ }
57
+
58
+ /** The one process call the probe makes — injected, so every case is stageable. */
59
+ export type SpawnVersion = (
60
+ program: string,
61
+ args: readonly string[],
62
+ options: { encoding: "utf8" },
63
+ ) => {
64
+ readonly status: number | null;
65
+ readonly stdout?: string | null;
66
+ readonly stderr?: string | null;
67
+ };
51
68
 
52
- /**
53
- * @param {object} deps
54
- * @param {Function} deps.spawnSync injected, so every case below is stageable
55
- * @param {string} [deps.program]
56
- * @param {string} [deps.characterized]
57
- * @returns {CliObservation}
58
- */
59
69
  export function observeAgentCli({
60
70
  spawnSync,
61
71
  program = "claude",
62
72
  characterized = CHARACTERIZED_CLI,
63
- }) {
73
+ }: {
74
+ spawnSync: SpawnVersion;
75
+ program?: string;
76
+ characterized?: string;
77
+ }): CliObservation {
64
78
  // 🔴 `encoding: "utf8"` IS THE FIX. With `stdio: "ignore"` — the shape this replaces — the
65
79
  // child's output goes nowhere and `probe.stdout` comes back null, so everything downstream
66
80
  // would be describing a string that was never read.
@@ -85,7 +99,7 @@ export function observeAgentCli({
85
99
  if (version === "")
86
100
  throw new Error(
87
101
  `\`${program} --version\` exited 0 but printed no version on stdout (got ${JSON.stringify(raw)}` +
88
- `${probe.stderr ? `, stderr ${JSON.stringify(String(probe.stderr).trim().slice(0, 200))}` : ""}). ` +
102
+ `${probe.stderr ? `, stderr ${JSON.stringify(probe.stderr.trim().slice(0, 200))}` : ""}). ` +
89
103
  `A presence probe is not an observation: this is the \`stdio: "ignore"\` shape that issue #7 ` +
90
104
  `is about, and a version nobody read must not be reported as one.`,
91
105
  );
package/lib/check.test.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { test } from "vitest";
3
- import { createChecker, failureMessage, renderDetail } from "./check.mjs";
3
+ import { createChecker, failureMessage, renderDetail } from "./check.ts";
4
4
 
5
5
  test("renderDetail: a string verbatim, nothing for empty, a value inspected, a thunk called", () => {
6
6
  assert.deepEqual(
@@ -32,7 +32,7 @@ test("createChecker: counts every call, stays silent on a pass", () => {
32
32
  const printed: string[] = [];
33
33
  const check = createChecker({ log: (s) => printed.push(s) });
34
34
  check("a", true);
35
- check("b", 1 === 1, "unused detail");
35
+ check("b", true, "unused detail");
36
36
  assert.deepEqual({ count: check.count, printed }, { count: 2, printed: [] });
37
37
  });
38
38
 
@@ -40,10 +40,15 @@ test("createChecker: a failure throws AND prints the label with its detail, and
40
40
  const printed: string[] = [];
41
41
  const check = createChecker({ log: (s) => printed.push(s) });
42
42
  // Guards: the detail reaches the reader — the drift this helper replaced dropped it.
43
- assert.throws(() => check("the label", false, { got: 3 }), {
44
- name: "AssertionError",
45
- message: "the label — { got: 3 }",
46
- });
43
+ assert.throws(
44
+ () => {
45
+ check("the label", false, { got: 3 });
46
+ },
47
+ {
48
+ name: "AssertionError",
49
+ message: "the label — { got: 3 }",
50
+ },
51
+ );
47
52
  assert.deepEqual(
48
53
  { count: check.count, printed },
49
54
  { count: 1, printed: ["✗ the label — { got: 3 }"] },
@@ -57,11 +62,11 @@ test("createChecker: a detail thunk is only evaluated on failure", () => {
57
62
  calls++;
58
63
  return "x";
59
64
  });
60
- assert.throws(() =>
65
+ assert.throws(() => {
61
66
  check("fail", false, () => {
62
67
  calls++;
63
68
  return "x";
64
- }),
65
- );
69
+ });
70
+ });
66
71
  assert.equal(calls, 1);
67
72
  });