paperlint 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +72 -0
- package/.github/workflows/ci.yml +297 -0
- package/.github/workflows/dependabot-automerge.yml +70 -0
- package/.github/workflows/pr-title.yml +59 -0
- package/.github/workflows/release.yml +54 -0
- package/CLAUDE.md +598 -0
- package/CONTRIBUTING.md +159 -0
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/action.harness.mjs +287 -0
- package/action.mutations.mjs +162 -0
- package/action.yml +138 -0
- package/bin/rpp.mjs +43 -0
- package/dist/action-ref.d.ts +12 -0
- package/dist/action-ref.d.ts.map +1 -0
- package/dist/action-ref.js +16 -0
- package/dist/action-ref.js.map +1 -0
- package/dist/adapters/banal/failure.d.ts +73 -0
- package/dist/adapters/banal/failure.d.ts.map +1 -0
- package/dist/adapters/banal/failure.js +58 -0
- package/dist/adapters/banal/failure.js.map +1 -0
- package/dist/adapters/banal/index.d.ts +17 -0
- package/dist/adapters/banal/index.d.ts.map +1 -0
- package/dist/adapters/banal/index.js +56 -0
- package/dist/adapters/banal/index.js.map +1 -0
- package/dist/adapters/banal/install.d.ts +26 -0
- package/dist/adapters/banal/install.d.ts.map +1 -0
- package/dist/adapters/banal/install.js +15 -0
- package/dist/adapters/banal/install.js.map +1 -0
- package/dist/adapters/banal/invocation.d.ts +48 -0
- package/dist/adapters/banal/invocation.d.ts.map +1 -0
- package/dist/adapters/banal/invocation.js +43 -0
- package/dist/adapters/banal/invocation.js.map +1 -0
- package/dist/adapters/banal/locate.d.ts +50 -0
- package/dist/adapters/banal/locate.d.ts.map +1 -0
- package/dist/adapters/banal/locate.js +34 -0
- package/dist/adapters/banal/locate.js.map +1 -0
- package/dist/adapters/banal/output.d.ts +27 -0
- package/dist/adapters/banal/output.d.ts.map +1 -0
- package/dist/adapters/banal/output.js +112 -0
- package/dist/adapters/banal/output.js.map +1 -0
- package/dist/adapters/banal/pin.d.ts +19 -0
- package/dist/adapters/banal/pin.d.ts.map +1 -0
- package/dist/adapters/banal/pin.js +15 -0
- package/dist/adapters/banal/pin.js.map +1 -0
- package/dist/adapters/banal/probe.d.ts +12 -0
- package/dist/adapters/banal/probe.d.ts.map +1 -0
- package/dist/adapters/banal/probe.js +27 -0
- package/dist/adapters/banal/probe.js.map +1 -0
- package/dist/adapters/banal/run.d.ts +89 -0
- package/dist/adapters/banal/run.d.ts.map +1 -0
- package/dist/adapters/banal/run.js +104 -0
- package/dist/adapters/banal/run.js.map +1 -0
- package/dist/adapters/banal/settings.d.ts +18 -0
- package/dist/adapters/banal/settings.d.ts.map +1 -0
- package/dist/adapters/banal/settings.js +29 -0
- package/dist/adapters/banal/settings.js.map +1 -0
- package/dist/adapters/banal/xml.d.ts +48 -0
- package/dist/adapters/banal/xml.d.ts.map +1 -0
- package/dist/adapters/banal/xml.js +67 -0
- package/dist/adapters/banal/xml.js.map +1 -0
- package/dist/adapters/curl/download.io.d.ts +14 -0
- package/dist/adapters/curl/download.io.d.ts.map +1 -0
- package/dist/adapters/curl/download.io.js +69 -0
- package/dist/adapters/curl/download.io.js.map +1 -0
- package/dist/adapters/curl/index.d.ts +6 -0
- package/dist/adapters/curl/index.d.ts.map +1 -0
- package/dist/adapters/curl/index.js +6 -0
- package/dist/adapters/curl/index.js.map +1 -0
- package/dist/adapters/memory/index.d.ts +43 -0
- package/dist/adapters/memory/index.d.ts.map +1 -0
- package/dist/adapters/memory/index.js +79 -0
- package/dist/adapters/memory/index.js.map +1 -0
- package/dist/adapters/node/files.io.d.ts +3 -0
- package/dist/adapters/node/files.io.d.ts.map +1 -0
- package/dist/adapters/node/files.io.js +31 -0
- package/dist/adapters/node/files.io.js.map +1 -0
- package/dist/adapters/node/host.io.d.ts +3 -0
- package/dist/adapters/node/host.io.d.ts.map +1 -0
- package/dist/adapters/node/host.io.js +14 -0
- package/dist/adapters/node/host.io.js.map +1 -0
- package/dist/adapters/node/index.d.ts +25 -0
- package/dist/adapters/node/index.d.ts.map +1 -0
- package/dist/adapters/node/index.js +14 -0
- package/dist/adapters/node/index.js.map +1 -0
- package/dist/adapters/node/process.io.d.ts +14 -0
- package/dist/adapters/node/process.io.d.ts.map +1 -0
- package/dist/adapters/node/process.io.js +41 -0
- package/dist/adapters/node/process.io.js.map +1 -0
- package/dist/adapters/node/workspace.io.d.ts +4 -0
- package/dist/adapters/node/workspace.io.d.ts.map +1 -0
- package/dist/adapters/node/workspace.io.js +33 -0
- package/dist/adapters/node/workspace.io.js.map +1 -0
- package/dist/adapters/pdfjs/fill.d.ts +42 -0
- package/dist/adapters/pdfjs/fill.d.ts.map +1 -0
- package/dist/adapters/pdfjs/fill.js +91 -0
- package/dist/adapters/pdfjs/fill.js.map +1 -0
- package/dist/build-engine.d.ts +48 -0
- package/dist/build-engine.d.ts.map +1 -0
- package/dist/build-engine.js +148 -0
- package/dist/build-engine.js.map +1 -0
- package/dist/build.d.ts +163 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +575 -0
- package/dist/build.js.map +1 -0
- package/dist/cli.d.ts +151 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +951 -0
- package/dist/cli.js.map +1 -0
- package/dist/doctor.d.ts +42 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +280 -0
- package/dist/doctor.js.map +1 -0
- package/dist/domain/geometry.d.ts +71 -0
- package/dist/domain/geometry.d.ts.map +1 -0
- package/dist/domain/geometry.js +35 -0
- package/dist/domain/geometry.js.map +1 -0
- package/dist/domain/host.d.ts +16 -0
- package/dist/domain/host.d.ts.map +1 -0
- package/dist/domain/host.js +8 -0
- package/dist/domain/host.js.map +1 -0
- package/dist/domain/page-layout.d.ts +34 -0
- package/dist/domain/page-layout.d.ts.map +1 -0
- package/dist/domain/page-layout.js +8 -0
- package/dist/domain/page-layout.js.map +1 -0
- package/dist/domain/paths.d.ts +5 -0
- package/dist/domain/paths.d.ts.map +1 -0
- package/dist/domain/paths.js +2 -0
- package/dist/domain/paths.js.map +1 -0
- package/dist/domain/result.d.ts +23 -0
- package/dist/domain/result.d.ts.map +1 -0
- package/dist/domain/result.js +10 -0
- package/dist/domain/result.js.map +1 -0
- package/dist/domain/sha256.d.ts +7 -0
- package/dist/domain/sha256.d.ts.map +1 -0
- package/dist/domain/sha256.js +14 -0
- package/dist/domain/sha256.js.map +1 -0
- package/dist/domain/text.d.ts +6 -0
- package/dist/domain/text.d.ts.map +1 -0
- package/dist/domain/text.js +7 -0
- package/dist/domain/text.js.map +1 -0
- package/dist/engine.d.ts +93 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +119 -0
- package/dist/engine.js.map +1 -0
- package/dist/exit-code.d.ts +22 -0
- package/dist/exit-code.d.ts.map +1 -0
- package/dist/exit-code.js +10 -0
- package/dist/exit-code.js.map +1 -0
- package/dist/facts-file.d.ts +96 -0
- package/dist/facts-file.d.ts.map +1 -0
- package/dist/facts-file.js +134 -0
- package/dist/facts-file.js.map +1 -0
- package/dist/hooks-settings.d.ts +141 -0
- package/dist/hooks-settings.d.ts.map +1 -0
- package/dist/hooks-settings.js +306 -0
- package/dist/hooks-settings.js.map +1 -0
- package/dist/init.d.ts +201 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +579 -0
- package/dist/init.js.map +1 -0
- package/dist/latex-log.d.ts +80 -0
- package/dist/latex-log.d.ts.map +1 -0
- package/dist/latex-log.js +187 -0
- package/dist/latex-log.js.map +1 -0
- package/dist/latex-loop.d.ts +129 -0
- package/dist/latex-loop.d.ts.map +1 -0
- package/dist/latex-loop.js +113 -0
- package/dist/latex-loop.js.map +1 -0
- package/dist/link-skills.d.ts +51 -0
- package/dist/link-skills.d.ts.map +1 -0
- package/dist/link-skills.js +199 -0
- package/dist/link-skills.js.map +1 -0
- package/dist/new-paper.d.ts +48 -0
- package/dist/new-paper.d.ts.map +1 -0
- package/dist/new-paper.js +110 -0
- package/dist/new-paper.js.map +1 -0
- package/dist/pdf-facts.d.ts +44 -0
- package/dist/pdf-facts.d.ts.map +1 -0
- package/dist/pdf-facts.js +239 -0
- package/dist/pdf-facts.js.map +1 -0
- package/dist/pdf-geometry.d.ts +170 -0
- package/dist/pdf-geometry.d.ts.map +1 -0
- package/dist/pdf-geometry.js +158 -0
- package/dist/pdf-geometry.js.map +1 -0
- package/dist/ports/download.d.ts +9 -0
- package/dist/ports/download.d.ts.map +1 -0
- package/dist/ports/download.js +2 -0
- package/dist/ports/download.js.map +1 -0
- package/dist/ports/files.d.ts +11 -0
- package/dist/ports/files.d.ts.map +1 -0
- package/dist/ports/files.js +2 -0
- package/dist/ports/files.js.map +1 -0
- package/dist/ports/measure-geometry.d.ts +8 -0
- package/dist/ports/measure-geometry.d.ts.map +1 -0
- package/dist/ports/measure-geometry.js +2 -0
- package/dist/ports/measure-geometry.js.map +1 -0
- package/dist/ports/process.d.ts +45 -0
- package/dist/ports/process.d.ts.map +1 -0
- package/dist/ports/process.js +2 -0
- package/dist/ports/process.js.map +1 -0
- package/dist/ports/tool-installer.d.ts +29 -0
- package/dist/ports/tool-installer.d.ts.map +1 -0
- package/dist/ports/tool-installer.js +2 -0
- package/dist/ports/tool-installer.js.map +1 -0
- package/dist/ports/workspace.d.ts +18 -0
- package/dist/ports/workspace.d.ts.map +1 -0
- package/dist/ports/workspace.js +2 -0
- package/dist/ports/workspace.js.map +1 -0
- package/dist/rules-config.d.ts +34 -0
- package/dist/rules-config.d.ts.map +1 -0
- package/dist/rules-config.js +132 -0
- package/dist/rules-config.js.map +1 -0
- package/dist/structure.d.ts +34 -0
- package/dist/structure.d.ts.map +1 -0
- package/dist/structure.js +149 -0
- package/dist/structure.js.map +1 -0
- package/dist/tex-requirements.d.ts +43 -0
- package/dist/tex-requirements.d.ts.map +1 -0
- package/dist/tex-requirements.js +127 -0
- package/dist/tex-requirements.js.map +1 -0
- package/dist/toolchain.d.ts +159 -0
- package/dist/toolchain.d.ts.map +1 -0
- package/dist/toolchain.js +542 -0
- package/dist/toolchain.js.map +1 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/configuration.md +235 -0
- package/docs/e2e.md +152 -0
- package/docs/incidents.md +59 -0
- package/docs/install.md +170 -0
- package/docs/optional-rules.md +107 -0
- package/docs/package-shape-options.md +262 -0
- package/docs/prior-art/README.md +76 -0
- package/docs/prior-art/blocking-vs-advisory.md +83 -0
- package/docs/prior-art/content-delivery.md +124 -0
- package/docs/prior-art/multi-mode-tools.md +106 -0
- package/docs/prior-art/nondeterministic-checks.md +99 -0
- package/docs/prior-art/package-location.md +422 -0
- package/docs/prior-art/paper-folder-scaffolding.md +538 -0
- package/docs/prior-art/readme-structure.md +69 -0
- package/docs/prior-art/repro/README.md +92 -0
- package/docs/prior-art/repro/claim1-allowedtools.mjs +66 -0
- package/docs/prior-art/repro/claim1-at2.mjs +40 -0
- package/docs/prior-art/repro/claim1-crosschannel.mjs +54 -0
- package/docs/prior-art/repro/claim1-frontmatter.mjs +76 -0
- package/docs/prior-art/repro/claim1-hook-payload-reporter.mjs +10 -0
- package/docs/prior-art/repro/claim1-plugin-frontmatter.mjs +27 -0
- package/docs/prior-art/repro/claim1-plugin-skill.mjs +52 -0
- package/docs/prior-art/repro/claim1-project-skill.mjs +81 -0
- package/docs/prior-art/repro/claim2-marketplace-flat-asclaimed.json +1 -0
- package/docs/prior-art/repro/claim2-marketplace-negative-control.json +1 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-exact.json +9 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-noversion.json +9 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-range.json +1 -0
- package/docs/prior-art/repro/claim3-imports.mjs +50 -0
- package/docs/prior-art/repro/claim4-find-package-json.mjs +8 -0
- package/docs/prior-art/repro/claim4-package-dir.mjs +39 -0
- package/docs/prior-art/repro/claim4-parent-arg.mjs +17 -0
- package/docs/prior-art/repro/claim4-resolve-apis.mjs +21 -0
- package/docs/prior-art/repro/claim4-setup-consumers.mjs +45 -0
- package/docs/prior-art/repro/claim4-yarn-pnp.mjs +70 -0
- package/docs/prior-art/repro/claim5-bin-launch.mjs +39 -0
- package/docs/prior-art/repro/claim5-exports-mutation.mjs +57 -0
- package/docs/prior-art/repro/claim5-resolved-location-and-bin.mjs +33 -0
- package/docs/prior-art/repro/claim6-candidate-ambiguity.mjs +17 -0
- package/docs/prior-art/repro/claim6-doc-path-candidates.mjs +27 -0
- package/docs/prior-art/test-tooling.md +131 -0
- package/docs/rules.md +58 -0
- package/docs/texlive-install-decision.md +230 -0
- package/docs/toolchain.md +152 -0
- package/eslint-rules/doc-fields.harness.mjs +336 -0
- package/eslint-rules/doc-fields.mjs +186 -0
- package/eslint-rules/doc-fields.mutations.mjs +96 -0
- package/eslint-rules/install-path-literals.harness.mjs +121 -0
- package/eslint-rules/install-path-literals.mjs +108 -0
- package/eslint-rules/install-path-literals.mutations.mjs +62 -0
- package/eslint-rules/latex-language.harness.mjs +599 -0
- package/eslint-rules/latex-language.mjs +591 -0
- package/eslint-rules/latex-language.mutations.mjs +196 -0
- package/eslint-rules/paper-research-question.harness.mjs +146 -0
- package/eslint-rules/paper-research-question.mjs +180 -0
- package/eslint-rules/paper-research-question.mutations.mjs +127 -0
- package/eslint-rules/paper-stages.harness.mjs +356 -0
- package/eslint-rules/paper-stages.mjs +455 -0
- package/eslint-rules/paper-stages.mutations.mjs +157 -0
- package/eslint-rules/paper-typography.harness.mjs +291 -0
- package/eslint-rules/paper-typography.mjs +313 -0
- package/eslint-rules/paper-typography.mutations.mjs +131 -0
- package/eslint-rules/papers.harness.mjs +259 -0
- package/eslint-rules/papers.mjs +166 -0
- package/eslint-rules/papers.mutations.mjs +186 -0
- package/eslint-rules/pdf-last-page-balance.harness.mjs +206 -0
- package/eslint-rules/pdf-last-page-balance.mjs +208 -0
- package/eslint-rules/review-findings-cause.harness.mjs +228 -0
- package/eslint-rules/review-findings-cause.mjs +135 -0
- package/eslint-rules/review-findings-cause.mutations.mjs +72 -0
- package/eslint-rules/temp-root-realpath.harness.mjs +176 -0
- package/eslint-rules/temp-root-realpath.mjs +129 -0
- package/eslint-rules/temp-root-realpath.mutations.mjs +99 -0
- package/eslint-rules/tex-build.harness.mjs +753 -0
- package/eslint-rules/tex-build.mjs +322 -0
- package/eslint-rules/tex-build.mutations.mjs +258 -0
- package/eslint.config.mjs +521 -0
- package/fixtures/build-e2e/acmart/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/acmart/paper.tex +11 -0
- package/fixtures/build-e2e/acmart/venue.json +1 -0
- package/fixtures/build-e2e/broken/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/broken/paper.tex +7 -0
- package/fixtures/build-e2e/cite/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/cite/build.sh +5 -0
- package/fixtures/build-e2e/cite/paper.tex +10 -0
- package/fixtures/build-e2e/cite/refs.bib +9 -0
- package/fixtures/build-e2e/empty/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/empty/paper.tex +6 -0
- package/fixtures/build-e2e/fallback/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/fallback/paper.tex +11 -0
- package/fixtures/build-e2e/guards/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/guards/paper.tex +10 -0
- package/fixtures/build-e2e/no-source/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/unbalanced/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/unbalanced/paper.tex +28 -0
- package/fixtures/build-e2e/unbalanced/refs.bib +269 -0
- package/fixtures/install-path-literals/clean.fixture.mjs +3 -0
- package/fixtures/install-path-literals/clean.md +15 -0
- package/fixtures/install-path-literals/defect.fixture.mjs +3 -0
- package/fixtures/install-path-literals/defect.md +14 -0
- package/fixtures/latex-language/clean.tex +50 -0
- package/fixtures/latex-language/defect.tex +52 -0
- package/fixtures/paper-research-question/comment-only/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-research-question/comment-only/paper.tex +7 -0
- package/fixtures/paper-research-question/declared-not-in-paper/PIPELINE-STATUS.md +10 -0
- package/fixtures/paper-research-question/declared-not-in-paper/paper.tex +6 -0
- package/fixtures/paper-research-question/draft/PIPELINE-STATUS.md +6 -0
- package/fixtures/paper-research-question/draft/paper.tex +2 -0
- package/fixtures/paper-research-question/markdown-no-rq/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-research-question/markdown-no-rq/paper.md +4 -0
- package/fixtures/paper-research-question/shipped-no-rq/PIPELINE-STATUS.md +12 -0
- package/fixtures/paper-research-question/shipped-no-rq/paper.tex +3 -0
- package/fixtures/paper-research-question/shipped-with-rq/PIPELINE-STATUS.md +10 -0
- package/fixtures/paper-research-question/shipped-with-rq/paper.tex +2 -0
- package/fixtures/paper-stages/authors-ran/PIPELINE-STATUS.md +16 -0
- package/fixtures/paper-stages/marker-in-prose/PIPELINE-STATUS.md +17 -0
- package/fixtures/paper-stages/nofile/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/noheader/PIPELINE-STATUS.md +1 -0
- package/fixtures/paper-stages/noheader/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/nothing/PIPELINE-STATUS.md +3 -0
- package/fixtures/paper-stages/ok/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-stages/ok/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/stale/PIPELINE-STATUS.md +1 -0
- package/fixtures/paper-stages/stale/versions/2026-07-22-submitted.STALE-WRONG-FILE.pdf +0 -0
- package/fixtures/paper-stages/twice/PIPELINE-STATUS.md +14 -0
- package/fixtures/paper-stages/twice/versions/2026-08-06-submitted.pdf +0 -0
- package/fixtures/paper-stages/twice/versions/2026-10-24-submitted.pdf +0 -0
- package/fixtures/paper-stages/undeclared/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/undeclared/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/undeclared/versions/2026-08-29-camera-ready.pdf +0 -0
- package/fixtures/paper-stages/wrongsize/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/wrongsize/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-typography/clean-paper/paper.tex +29 -0
- package/fixtures/paper-typography/messy-paper/paper.tex +27 -0
- package/fixtures/pdf-facts/README.md +22 -0
- package/fixtures/pdf-facts/corrupt-font.pdf +0 -0
- package/fixtures/pdf-facts/encrypted.pdf +0 -0
- package/fixtures/pdf-facts/hidden-text.pdf +0 -0
- package/fixtures/pdf-facts/hidden-text.tex +28 -0
- package/fixtures/pdf-facts/t3-all.pdf +0 -0
- package/fixtures/pdf-facts/t3-all.tex +8 -0
- package/fixtures/pdf-facts/t3-mixed.pdf +0 -0
- package/fixtures/pdf-facts/t3-mixed.tex +9 -0
- package/fixtures/pdf-facts/ttf.pdf +2240 -1
- package/fixtures/pdf-facts/ttf.tex +6 -0
- package/fixtures/real-markdown-paper/baseline.json +24 -0
- package/fixtures/real-markdown-paper/baseline.mjs +48 -0
- package/fixtures/render-paper/build-clean.sh +25 -0
- package/fixtures/render-paper/build-defect.sh +15 -0
- package/fixtures/review-findings-cause/clean.md +17 -0
- package/fixtures/review-findings-cause/defect.md +14 -0
- package/fixtures/review-findings-cause/old-debt.md +14 -0
- package/fixtures/review-findings-cause/quiet-in-fence.md +16 -0
- package/fixtures/tex-build/clean.tex +21 -0
- package/fixtures/tex-build/defect.tex +24 -0
- package/fixtures/tex-build/frontmatter-clean.tex +25 -0
- package/fixtures/tex-build/frontmatter-defect.tex +23 -0
- package/fixtures/toolchain-mirror/catalog.txt +5 -0
- package/fixtures/toolchain-mirror/install-tl +27 -0
- package/fixtures/toolchain-mirror/release-texlive.txt +3 -0
- package/fixtures/toolchain-mirror/release-year +1 -0
- package/fixtures/toolchain-mirror/stub-kpsewhich +8 -0
- package/fixtures/toolchain-mirror/stub-pdflatex +3 -0
- package/fixtures/toolchain-mirror/stub-tlmgr +44 -0
- package/hooks/hooks.harness.mjs +713 -0
- package/hooks/hooks.mutations.mjs +337 -0
- package/hooks/paper-edit-guard.hook.d.mts +13 -0
- package/hooks/paper-edit-guard.hook.mjs +457 -0
- package/hooks/paper-skills-nudge.hook.mjs +136 -0
- package/hooks/paper-status-gates.hook.mjs +156 -0
- package/hooks/paper-status-gates.sh +91 -0
- package/lib/agent-cli-version.harness.mjs +165 -0
- package/lib/agent-cli-version.mjs +106 -0
- package/lib/agent-cli-version.mutations.mjs +109 -0
- package/lib/markdown.mjs +386 -0
- package/lib/mutation-driver.harness.mjs +227 -0
- package/lib/mutation-driver.mjs +397 -0
- package/lib/mutation-driver.mutations.mjs +68 -0
- package/lib/paper-config.d.mts +34 -0
- package/lib/paper-config.harness.mjs +286 -0
- package/lib/paper-config.mjs +142 -0
- package/lib/paper-config.mutations.mjs +143 -0
- package/lib/skill-checks.mjs +701 -0
- package/lib/skill-corpus.mjs +403 -0
- package/lib/skill-eval-fixture.mjs +63 -0
- package/lib/skill-eval-kit.mjs +257 -0
- package/lib/skill-trigger-cases.harness.mjs +170 -0
- package/lib/skill-trigger-cases.mjs +446 -0
- package/lib/skill-trigger-cases.mutations.mjs +65 -0
- package/lib/trigger-ledger.mjs +215 -0
- package/package.json +97 -0
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/hooks/hooks.json +30 -0
- package/scripts/check.harness.mjs +177 -0
- package/scripts/check.mjs +239 -0
- package/scripts/check.mutations.mjs +110 -0
- package/scripts/eslint-report-guard.mjs +82 -0
- package/scripts/exclusive.mjs +138 -0
- package/scripts/harness-api.frozen.json +76 -0
- package/scripts/harness-api.test.ts +175 -0
- package/scripts/layer-legacy-frozen.d.mts +28 -0
- package/scripts/layer-legacy-frozen.mjs +152 -0
- package/scripts/layer-legacy-frozen.test.ts +115 -0
- package/scripts/layer-legacy.frozen.json +50 -0
- package/scripts/mutation-batteries-frozen.harness.mjs +204 -0
- package/scripts/mutation-batteries-frozen.mjs +238 -0
- package/scripts/mutation-batteries.frozen.json +117 -0
- package/scripts/release-config.test.ts +90 -0
- package/scripts/rules-are-content-only.harness.mjs +113 -0
- package/scripts/rules-are-content-only.mjs +138 -0
- package/scripts/rules-are-content-only.mutations.mjs +81 -0
- package/scripts/rules-see-files.harness.mjs +115 -0
- package/scripts/rules-see-files.mjs +99 -0
- package/scripts/rules-see-files.mutations.mjs +131 -0
- package/scripts/run-mutations.mjs +100 -0
- package/scripts/semantic-release-plugins.d.ts +16 -0
- package/skills/README.md +15 -0
- package/skills/analyze-sibling-paper/SKILL.md +170 -0
- package/skills/analyze-sibling-paper/SKILL.md.spec.ts +186 -0
- package/skills/analyze-sibling-paper/analyze-sibling-paper.eval.mjs +19 -0
- package/skills/analyze-sibling-paper/analyze-sibling-paper.harness.mjs +23 -0
- package/skills/argument-arc/SKILL.md +177 -0
- package/skills/argument-arc/SKILL.md.spec.ts +192 -0
- package/skills/argument-arc/argument-arc.eval.mjs +19 -0
- package/skills/argument-arc/argument-arc.harness.mjs +23 -0
- package/skills/build-benchmark/SKILL.md +213 -0
- package/skills/build-benchmark/SKILL.md.spec.ts +220 -0
- package/skills/build-benchmark/build-benchmark.eval.mjs +19 -0
- package/skills/build-benchmark/build-benchmark.harness.mjs +23 -0
- package/skills/build-benchmark/references/adversarial-cold-repro.md +68 -0
- package/skills/camera-ready/SKILL.md +148 -0
- package/skills/camera-ready/SKILL.md.spec.ts +164 -0
- package/skills/camera-ready/camera-ready.eval.mjs +19 -0
- package/skills/camera-ready/camera-ready.harness.mjs +23 -0
- package/skills/cold-read-diff/SKILL.md +160 -0
- package/skills/cold-read-diff/SKILL.md.spec.ts +166 -0
- package/skills/cold-read-diff/cold-read-diff.eval.mjs +19 -0
- package/skills/cold-read-diff/cold-read-diff.harness.mjs +23 -0
- package/skills/draft-paper/SKILL.md +152 -0
- package/skills/draft-paper/SKILL.md.spec.ts +169 -0
- package/skills/draft-paper/draft-paper.eval.mjs +19 -0
- package/skills/draft-paper/draft-paper.harness.mjs +23 -0
- package/skills/extend-paper/SKILL.md +99 -0
- package/skills/extend-paper/SKILL.md.spec.ts +116 -0
- package/skills/extend-paper/extend-paper.eval.mjs +19 -0
- package/skills/extend-paper/extend-paper.harness.mjs +23 -0
- package/skills/find-venue/SKILL.md +128 -0
- package/skills/find-venue/SKILL.md.spec.ts +145 -0
- package/skills/find-venue/find-venue.eval.mjs +19 -0
- package/skills/find-venue/find-venue.harness.mjs +23 -0
- package/skills/grade-paper-writing/SKILL.md +436 -0
- package/skills/grade-paper-writing/SKILL.md.spec.ts +453 -0
- package/skills/grade-paper-writing/fixtures/control_gopen.txt +1 -0
- package/skills/grade-paper-writing/fixtures/control_human_paper.txt +1 -0
- package/skills/grade-paper-writing/fixtures/rewrite.txt +1 -0
- package/skills/grade-paper-writing/fixtures/specimen.txt +1 -0
- package/skills/grade-paper-writing/fixtures/structure-checks.md +22 -0
- package/skills/grade-paper-writing/grade-paper-writing.eval.mjs +19 -0
- package/skills/grade-paper-writing/grade-paper-writing.harness.mjs +23 -0
- package/skills/grade-paper-writing/prose-lint.mjs +713 -0
- package/skills/harden-paper/SKILL.md +318 -0
- package/skills/harden-paper/SKILL.md.spec.ts +336 -0
- package/skills/harden-paper/check-numbers.sh +33 -0
- package/skills/harden-paper/check-release-claims.sh +35 -0
- package/skills/harden-paper/fixtures/uncited-assertions-sample.md +43 -0
- package/skills/harden-paper/fixtures/uncited-assertions-sample.tex +77 -0
- package/skills/harden-paper/harden-paper.eval.mjs +19 -0
- package/skills/harden-paper/harden-paper.harness.mjs +23 -0
- package/skills/map-prior-work/SKILL.md +211 -0
- package/skills/map-prior-work/SKILL.md.spec.ts +227 -0
- package/skills/map-prior-work/map-prior-work.eval.mjs +19 -0
- package/skills/map-prior-work/map-prior-work.harness.mjs +23 -0
- package/skills/osf-artifact-upload/SKILL.md +52 -0
- package/skills/osf-artifact-upload/SKILL.md.spec.ts +59 -0
- package/skills/osf-artifact-upload/osf-artifact-upload.eval.mjs +22 -0
- package/skills/osf-artifact-upload/osf-artifact-upload.harness.mjs +103 -0
- package/skills/paper-adversarial-review/SKILL.md +126 -0
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +142 -0
- package/skills/paper-adversarial-review/paper-adversarial-review.eval.mjs +19 -0
- package/skills/paper-adversarial-review/paper-adversarial-review.harness.mjs +23 -0
- package/skills/paper-pipeline/PIPELINE-MAP.md +371 -0
- package/skills/paper-pipeline/SKILL.md +499 -0
- package/skills/paper-pipeline/SKILL.md.spec.ts +517 -0
- package/skills/paper-pipeline/description-language.eval.mjs +347 -0
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +891 -0
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +1254 -0
- package/skills/paper-pipeline/paper-pipeline.eval.mjs +22 -0
- package/skills/paper-pipeline/paper-pipeline.harness.mjs +143 -0
- package/skills/paper-pipeline/pipeline-firing.baseline.json +270 -0
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +664 -0
- package/skills/paper-pipeline/pipeline-language.eval.mjs +672 -0
- package/skills/paper-pipeline/references/acceptance-gate.md +329 -0
- package/skills/paper-pipeline/references/acl-venue-rules.md +142 -0
- package/skills/paper-pipeline/references/anonymization.md +68 -0
- package/skills/paper-pipeline/references/artifact-checklist.md +93 -0
- package/skills/paper-pipeline/references/body-vs-appendix.md +97 -0
- package/skills/paper-pipeline/references/credit-criteria.md +69 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/README.md +35 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/run_retext.mjs +24 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/sentences.txt +11 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/test_sentences.py +25 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-prose-checkers.md +538 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-reproducible-tooling.md +431 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-staleness-and-orchestration.md +592 -0
- package/skills/paper-pipeline/references/pipeline-status-template.md +162 -0
- package/skills/paper-pipeline/references/review-ratchet.md +36 -0
- package/skills/paper-pipeline/references/sweep-2026-08-09-ideal-pipeline.md +585 -0
- package/skills/paper-pipeline/references/writing-craft.md +448 -0
- package/skills/paper-pipeline/repro/2026-08-07-description-language-control.log +63 -0
- package/skills/paper-pipeline/repro/2026-08-07-fork-check.log +52 -0
- package/skills/paper-pipeline/repro/2026-08-07-fork-check2.log +33 -0
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-pilot.log +33 -0
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-raw.log +166 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.json +338 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.log +118 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-raw.log +245 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary.json +776 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-oracle.log +53 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-raw.log +89 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.json +450 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.log +136 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-raw.log +242 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-setupdiff.log +59 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation.json +1032 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.json +139 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.log +98 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication.mjs +92 -0
- package/skills/paper-pipeline/repro/README.md +129 -0
- package/skills/paper-pipeline/repro/analyze-language-eval.py +116 -0
- package/skills/paper-pipeline/scripts/README.md +344 -0
- package/skills/paper-pipeline/scripts/announce.mjs +67 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +496 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +397 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.mutations.mjs +218 -0
- package/skills/paper-pipeline/scripts/check-provenance.mjs +184 -0
- package/skills/paper-pipeline/scripts/consumer.d.mts +32 -0
- package/skills/paper-pipeline/scripts/consumer.harness.mjs +562 -0
- package/skills/paper-pipeline/scripts/consumer.mjs +535 -0
- package/skills/paper-pipeline/scripts/consumer.mutations.mjs +190 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +457 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +656 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.mutations.mjs +54 -0
- package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +51 -0
- package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +52 -0
- package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +6 -0
- package/skills/paper-pipeline/scripts/fixtures/real-bib/refs.bib +153 -0
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +466 -0
- package/skills/paper-pipeline/scripts/generated-code.mjs +338 -0
- package/skills/paper-pipeline/scripts/generated-code.mutations.mjs +254 -0
- package/skills/paper-pipeline/scripts/ledger.mjs +623 -0
- package/skills/paper-pipeline/scripts/ledger.selftest.mjs +286 -0
- package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +389 -0
- package/skills/paper-pipeline/scripts/pipeline-check.mjs +737 -0
- package/skills/paper-pipeline/scripts/pipeline-check.mutations.mjs +54 -0
- package/skills/paper-pipeline/scripts/pipeline-edges.mjs +169 -0
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +178 -0
- package/skills/paper-pipeline/scripts/population-map.mjs +181 -0
- package/skills/paper-pipeline/scripts/population-map.mutations.mjs +65 -0
- package/skills/paper-pipeline/scripts/population-map.selftest.mjs +122 -0
- package/skills/paper-pipeline/scripts/provenance.harness.mjs +240 -0
- package/skills/paper-pipeline/scripts/provenance.mutations.mjs +59 -0
- package/skills/paper-pipeline/scripts/round-diff.harness.mjs +881 -0
- package/skills/paper-pipeline/scripts/round-diff.mjs +576 -0
- package/skills/paper-pipeline/scripts/round-diff.mutations.mjs +276 -0
- package/skills/paper-pipeline/scripts/run-mechanical.mjs +633 -0
- package/skills/paper-pipeline/scripts/status.mjs +295 -0
- package/skills/paper-status/SKILL.md +183 -0
- package/skills/paper-status/SKILL.md.spec.ts +190 -0
- package/skills/paper-status/paper-status.eval.mjs +22 -0
- package/skills/paper-status/paper-status.harness.mjs +25 -0
- package/skills/pc-panel-review/SKILL.md +263 -0
- package/skills/pc-panel-review/SKILL.md.spec.ts +280 -0
- package/skills/pc-panel-review/pc-panel-review.eval.mjs +19 -0
- package/skills/pc-panel-review/pc-panel-review.harness.mjs +23 -0
- package/skills/plan-paper-timeline/SKILL.md +182 -0
- package/skills/plan-paper-timeline/SKILL.md.spec.ts +200 -0
- package/skills/plan-paper-timeline/fixtures/fake-google-calendar.mjs +239 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +431 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.mutations.mjs +65 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.eval.mjs +19 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.harness.mjs +23 -0
- package/skills/render-paper/SKILL.md +159 -0
- package/skills/render-paper/SKILL.md.spec.ts +166 -0
- package/skills/render-paper/check-render.sh +419 -0
- package/skills/render-paper/checkers-requirements.txt +55 -0
- package/skills/render-paper/ensure-checkers.sh +69 -0
- package/skills/render-paper/extract-pdf-facts.harness.mjs +166 -0
- package/skills/render-paper/extract-pdf-facts.mjs +144 -0
- package/skills/render-paper/render-paper.eval.mjs +19 -0
- package/skills/render-paper/render-paper.harness.mjs +339 -0
- package/skills/research-ideate/SKILL.md +136 -0
- package/skills/research-ideate/SKILL.md.spec.ts +152 -0
- package/skills/research-ideate/research-ideate.eval.mjs +19 -0
- package/skills/research-ideate/research-ideate.harness.mjs +23 -0
- package/skills/skill-contract.mutations.mjs +179 -0
- package/skills/study-accepted-papers/SKILL.md +206 -0
- package/skills/study-accepted-papers/SKILL.md.spec.ts +223 -0
- package/skills/study-accepted-papers/study-accepted-papers.eval.mjs +19 -0
- package/skills/study-accepted-papers/study-accepted-papers.harness.mjs +23 -0
- package/skills/submit-paper/SKILL.md +182 -0
- package/skills/submit-paper/SKILL.md.spec.ts +199 -0
- package/skills/submit-paper/check-deanon.sh +149 -0
- package/skills/submit-paper/references/publishers/acm.md +92 -0
- package/skills/submit-paper/references/venues/agenticdev.jsonc +108 -0
- package/skills/submit-paper/references/venues/agenticdev.md +139 -0
- package/skills/submit-paper/references/venues/agenticdev.tex +19 -0
- package/skills/submit-paper/references/venues/aisec.jsonc +101 -0
- package/skills/submit-paper/references/venues/aisec.md +105 -0
- package/skills/submit-paper/references/venues/paper-guards.tex +41 -0
- package/skills/submit-paper/references/venues/realm.jsonc +81 -0
- package/skills/submit-paper/references/venues/realm.md +155 -0
- package/skills/submit-paper/references/venues/tex-base.jsonc +50 -0
- package/skills/submit-paper/references/venues/venue-profile.schema.json +74 -0
- package/skills/submit-paper/submit-paper.eval.mjs +19 -0
- package/skills/submit-paper/submit-paper.harness.mjs +23 -0
- package/skills/sweep-design-space/SKILL.md +269 -0
- package/skills/sweep-design-space/SKILL.md.spec.ts +285 -0
- package/skills/sweep-design-space/sweep-design-space.eval.mjs +19 -0
- package/skills/sweep-design-space/sweep-design-space.harness.mjs +23 -0
- package/skills/tighten-paper/SKILL.md +368 -0
- package/skills/tighten-paper/SKILL.md.spec.ts +384 -0
- package/skills/tighten-paper/structure.mjs +371 -0
- package/skills/tighten-paper/tighten-paper.eval.mjs +19 -0
- package/skills/tighten-paper/tighten-paper.harness.mjs +23 -0
- package/skills/verify-citations/SKILL.md +328 -0
- package/skills/verify-citations/SKILL.md.spec.ts +345 -0
- package/skills/verify-citations/scripts/bib-authors.mjs +479 -0
- package/skills/verify-citations/scripts/bib-authors.test.mjs +175 -0
- package/skills/verify-citations/scripts/verify-cites.mjs +1108 -0
- package/skills/verify-citations/scripts/verify-cites.test.mjs +735 -0
- package/skills/verify-citations/verify-citations.eval.mjs +19 -0
- package/skills/verify-citations/verify-citations.harness.mjs +23 -0
- package/src/CLAUDE.md +51 -0
- package/src/action-ref.test.ts +26 -0
- package/src/action-ref.ts +15 -0
- package/src/adapters/banal/failure.test.ts +63 -0
- package/src/adapters/banal/failure.ts +118 -0
- package/src/adapters/banal/index.test.ts +119 -0
- package/src/adapters/banal/index.ts +100 -0
- package/src/adapters/banal/install.test.ts +20 -0
- package/src/adapters/banal/install.ts +41 -0
- package/src/adapters/banal/invocation.test.ts +74 -0
- package/src/adapters/banal/invocation.ts +95 -0
- package/src/adapters/banal/locate.test.ts +52 -0
- package/src/adapters/banal/locate.ts +84 -0
- package/src/adapters/banal/output.test.ts +140 -0
- package/src/adapters/banal/output.ts +141 -0
- package/src/adapters/banal/pin.ts +30 -0
- package/src/adapters/banal/probe.ts +35 -0
- package/src/adapters/banal/run.test.ts +191 -0
- package/src/adapters/banal/run.ts +244 -0
- package/src/adapters/banal/settings.test.ts +31 -0
- package/src/adapters/banal/settings.ts +55 -0
- package/src/adapters/banal/xml.test.ts +111 -0
- package/src/adapters/banal/xml.ts +112 -0
- package/src/adapters/curl/download.io.ts +73 -0
- package/src/adapters/curl/download.test.ts +55 -0
- package/src/adapters/curl/index.ts +5 -0
- package/src/adapters/memory/index.ts +131 -0
- package/src/adapters/node/files.io.ts +39 -0
- package/src/adapters/node/files.test.ts +28 -0
- package/src/adapters/node/host.io.ts +15 -0
- package/src/adapters/node/index.ts +36 -0
- package/src/adapters/node/process.io.ts +49 -0
- package/src/adapters/node/process.test.ts +46 -0
- package/src/adapters/node/workspace.io.ts +40 -0
- package/src/adapters/node/workspace.test.ts +58 -0
- package/src/adapters/pdfjs/fill.test.ts +111 -0
- package/src/adapters/pdfjs/fill.ts +141 -0
- package/src/build-engine.harness.mjs +314 -0
- package/src/build-engine.ts +219 -0
- package/src/build.harness.mjs +631 -0
- package/src/build.mutations.mjs +195 -0
- package/src/build.ts +793 -0
- package/src/cli.harness.mjs +2007 -0
- package/src/cli.mutations.mjs +448 -0
- package/src/cli.ts +1189 -0
- package/src/doctor.harness.mjs +396 -0
- package/src/doctor.mutations.mjs +175 -0
- package/src/doctor.ts +356 -0
- package/src/domain/geometry.ts +108 -0
- package/src/domain/host.ts +23 -0
- package/src/domain/page-layout.ts +32 -0
- package/src/domain/paths.ts +5 -0
- package/src/domain/result.test.ts +26 -0
- package/src/domain/result.ts +29 -0
- package/src/domain/sha256.test.ts +12 -0
- package/src/domain/sha256.ts +21 -0
- package/src/domain/text.ts +11 -0
- package/src/engine.harness.mjs +252 -0
- package/src/engine.ts +176 -0
- package/src/exit-code.test.ts +21 -0
- package/src/exit-code.ts +38 -0
- package/src/facts-file.test.ts +240 -0
- package/src/facts-file.ts +241 -0
- package/src/hooks-settings.harness.mjs +386 -0
- package/src/hooks-settings.mutations.mjs +116 -0
- package/src/hooks-settings.ts +434 -0
- package/src/init.ts +900 -0
- package/src/latex-log.harness.mjs +226 -0
- package/src/latex-log.ts +234 -0
- package/src/latex-loop.harness.mjs +449 -0
- package/src/latex-loop.ts +211 -0
- package/src/link-skills.harness.mjs +273 -0
- package/src/link-skills.mutations.mjs +136 -0
- package/src/link-skills.ts +258 -0
- package/src/new-paper.harness.mjs +216 -0
- package/src/new-paper.mutations.mjs +79 -0
- package/src/new-paper.ts +158 -0
- package/src/pdf-facts.harness.mjs +188 -0
- package/src/pdf-facts.ts +327 -0
- package/src/pdf-geometry.harness.mjs +254 -0
- package/src/pdf-geometry.ts +300 -0
- package/src/ports/download.ts +10 -0
- package/src/ports/files.ts +11 -0
- package/src/ports/measure-geometry.ts +8 -0
- package/src/ports/process.ts +46 -0
- package/src/ports/tool-installer.ts +33 -0
- package/src/ports/workspace.ts +20 -0
- package/src/rules-config.harness.mjs +114 -0
- package/src/rules-config.ts +178 -0
- package/src/structure.harness.mjs +179 -0
- package/src/structure.mutations.mjs +83 -0
- package/src/structure.ts +166 -0
- package/src/tex-requirements.harness.mjs +238 -0
- package/src/tex-requirements.ts +181 -0
- package/src/toolchain.harness.mjs +651 -0
- package/src/toolchain.ts +755 -0
- package/src/types.ts +106 -0
- package/templates/paper/PIPELINE-STATUS.md +72 -0
- package/templates/paper/paper.md +4 -0
- package/templates/paper/paper.tex +8 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `latex-log.ts` — reading pdflatex logs, bibtex output and `.aux` files, both halves.
|
|
3
|
+
*
|
|
4
|
+
* The log excerpts below are copied from real TeX Live 2023 runs (2026-09-24), not typed from
|
|
5
|
+
* memory: the two error spellings `-file-line-error` produces, the missing-package form, and a
|
|
6
|
+
* marker that TeX broke at column 79.
|
|
7
|
+
*/
|
|
8
|
+
import assert from "node:assert/strict";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
12
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const {
|
|
14
|
+
unwrapLog,
|
|
15
|
+
logMarkers,
|
|
16
|
+
errorExcerpt,
|
|
17
|
+
isErrorLine,
|
|
18
|
+
bibtexExcerpt,
|
|
19
|
+
auxBib,
|
|
20
|
+
MAX_PRINT_LINE,
|
|
21
|
+
} = await import(join(HERE, "latex-log.ts"));
|
|
22
|
+
|
|
23
|
+
let n = 0;
|
|
24
|
+
const check = (label, cond) => {
|
|
25
|
+
assert.ok(cond, label);
|
|
26
|
+
n++;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// ── the 79-column wrap ──────────────────────────────────────────────────────────────────
|
|
30
|
+
// A package warning long enough that "Rerun to get" crosses column 79. A real acmart log shows the
|
|
31
|
+
// same break: "Document Class: acmart 2024/02/04 v2.03 Typesetting articles for the Associatio"
|
|
32
|
+
// is a 79-byte line continued on the next one.
|
|
33
|
+
const warning =
|
|
34
|
+
"Package natbib Warning: Citation(s) may have changed in the bibliography. Rerun to get citations correct.";
|
|
35
|
+
const at79 = warning.slice(0, MAX_PRINT_LINE);
|
|
36
|
+
const rest = warning.slice(MAX_PRINT_LINE);
|
|
37
|
+
check(
|
|
38
|
+
"the fixture really is split at 79 (the marker straddles the break)",
|
|
39
|
+
at79.length === 79 && !at79.includes("Rerun to get") && rest.length > 0,
|
|
40
|
+
);
|
|
41
|
+
const wrappedLog = ["(./paper.aux)", "", at79, rest, ""].join("\n");
|
|
42
|
+
// Guards: rejoining the lines TeX broke — a 'Rerun to get' split across the break is on no line at
|
|
43
|
+
// all, and the loop would stop one pass early.
|
|
44
|
+
check(
|
|
45
|
+
"🔴 a marker broken at column 79 is rejoined and found",
|
|
46
|
+
logMarkers(unwrapLog(wrappedLog)).includes("rerun-requested"),
|
|
47
|
+
);
|
|
48
|
+
check(
|
|
49
|
+
"without the unwrap, the split marker is NOT on any one line — the wrap is real",
|
|
50
|
+
!wrappedLog.split("\n").some((l) => l.includes("Rerun to get")),
|
|
51
|
+
);
|
|
52
|
+
// LaTeX core's line is 78 bytes; one trailing space makes it a line TeX would NOT have broken but
|
|
53
|
+
// that is 79 bytes anyway — the ambiguous case `unwrapLog` documents.
|
|
54
|
+
const exact79 =
|
|
55
|
+
"LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right. ";
|
|
56
|
+
check(
|
|
57
|
+
"a marker line that is exactly 79 bytes by itself is still found after the (harmless) join",
|
|
58
|
+
exact79.length === 79 &&
|
|
59
|
+
logMarkers(unwrapLog([exact79, "", "next"].join("\n"))).includes(
|
|
60
|
+
"labels-changed",
|
|
61
|
+
),
|
|
62
|
+
);
|
|
63
|
+
check(
|
|
64
|
+
"a short line is not joined to the next one",
|
|
65
|
+
JSON.stringify(unwrapLog("a\nb")) === JSON.stringify(["a", "b"]),
|
|
66
|
+
);
|
|
67
|
+
check(
|
|
68
|
+
"two consecutive 79-byte lines join with the third",
|
|
69
|
+
unwrapLog(["x".repeat(79), "y".repeat(79), "z"].join("\n"))[0] ===
|
|
70
|
+
"x".repeat(79) + "y".repeat(79) + "z",
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// ── markers ─────────────────────────────────────────────────────────────────────────────
|
|
74
|
+
const firstPass = [
|
|
75
|
+
"LaTeX Warning: Reference `sec:intro' on page 1 undefined on input line 4.",
|
|
76
|
+
"LaTeX Warning: There were undefined references.",
|
|
77
|
+
"LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.",
|
|
78
|
+
"Package rerunfilecheck Warning: File `paper.out' has changed.",
|
|
79
|
+
"(rerunfilecheck) Rerun to get outlines right",
|
|
80
|
+
"Package natbib Warning: There were undefined citations.",
|
|
81
|
+
];
|
|
82
|
+
const m = logMarkers(firstPass);
|
|
83
|
+
check(
|
|
84
|
+
"every documented marker is recognised",
|
|
85
|
+
[
|
|
86
|
+
"rerun-requested",
|
|
87
|
+
"labels-changed",
|
|
88
|
+
"rerunfilecheck",
|
|
89
|
+
"undefined-references",
|
|
90
|
+
"undefined-citations",
|
|
91
|
+
].every((k) => m.includes(k)),
|
|
92
|
+
);
|
|
93
|
+
check(
|
|
94
|
+
"a clean log carries no markers",
|
|
95
|
+
logMarkers([
|
|
96
|
+
"This is pdfTeX",
|
|
97
|
+
"Output written on paper.pdf (1 page, 1234 bytes).",
|
|
98
|
+
]).length === 0,
|
|
99
|
+
);
|
|
100
|
+
// Guards: matching the documented summary line rather than the word 'undefined'.
|
|
101
|
+
check(
|
|
102
|
+
"a single reference warning is not the end-of-run summary",
|
|
103
|
+
!logMarkers([firstPass[0]]).includes("undefined-references"),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// ── error excerpt ───────────────────────────────────────────────────────────────────────
|
|
107
|
+
const undefinedCs = [
|
|
108
|
+
"(/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo)",
|
|
109
|
+
"./paper.tex:4: Undefined control sequence.",
|
|
110
|
+
"l.4 \\foo",
|
|
111
|
+
" bar baz ",
|
|
112
|
+
"Here is how much of TeX's memory you used:",
|
|
113
|
+
];
|
|
114
|
+
const ex = errorExcerpt(undefinedCs);
|
|
115
|
+
// Guards: the spelling rpp's own flags produce for most errors — with only the `!` form recognised,
|
|
116
|
+
// an undefined control sequence fails with no error line quoted.
|
|
117
|
+
check(
|
|
118
|
+
"-file-line-error form: the error line, the l.NNN line and the rest of the source line",
|
|
119
|
+
ex[0] === "./paper.tex:4: Undefined control sequence." &&
|
|
120
|
+
ex[1] === "l.4 \\foo" &&
|
|
121
|
+
ex[2].includes("bar baz"),
|
|
122
|
+
);
|
|
123
|
+
// Guards: the end of the excerpt — without it the excerpt runs into TeX's memory statistics and
|
|
124
|
+
// buries the line the author needs.
|
|
125
|
+
check(
|
|
126
|
+
"the excerpt stops at the context — the memory statistics are not quoted",
|
|
127
|
+
!ex.some((l) => l.startsWith("Here is how much")),
|
|
128
|
+
);
|
|
129
|
+
const missingPkg = [
|
|
130
|
+
")",
|
|
131
|
+
"",
|
|
132
|
+
"! LaTeX Error: File `nopepkg.sty' not found.",
|
|
133
|
+
"",
|
|
134
|
+
"Type X to quit or <RETURN> to proceed,",
|
|
135
|
+
"or enter new name. (Default extension: sty)",
|
|
136
|
+
"",
|
|
137
|
+
"Enter file name: ",
|
|
138
|
+
"./paper.tex:3: Emergency stop.",
|
|
139
|
+
"<read *> ",
|
|
140
|
+
" ",
|
|
141
|
+
"l.3 \\begin",
|
|
142
|
+
" {document}^^M ",
|
|
143
|
+
"Here is how much of TeX's memory you used:",
|
|
144
|
+
];
|
|
145
|
+
const ex2 = errorExcerpt(missingPkg);
|
|
146
|
+
check(
|
|
147
|
+
"the classic `!` form: the FIRST error is quoted, not the emergency stop after it",
|
|
148
|
+
ex2[0] === "! LaTeX Error: File `nopepkg.sty' not found.",
|
|
149
|
+
);
|
|
150
|
+
check(
|
|
151
|
+
"…and the excerpt runs to its l.NNN context, dropping blank lines",
|
|
152
|
+
ex2.includes("l.3 \\begin") && !ex2.some((l) => l.trim() === ""),
|
|
153
|
+
);
|
|
154
|
+
check(
|
|
155
|
+
"no error line — an empty excerpt",
|
|
156
|
+
errorExcerpt(["all fine"]).length === 0,
|
|
157
|
+
);
|
|
158
|
+
check(
|
|
159
|
+
"a warning that ends in `line 4.` is not an error line",
|
|
160
|
+
!isErrorLine(
|
|
161
|
+
"LaTeX Warning: Citation `k' on page 1 undefined on input line 4.",
|
|
162
|
+
),
|
|
163
|
+
);
|
|
164
|
+
// Guards: the requirement that the prefix look like a path — without it, the first such line in a
|
|
165
|
+
// package's info output is quoted as 'the error'.
|
|
166
|
+
check(
|
|
167
|
+
"a path-less `word:12: ` is not an error line either",
|
|
168
|
+
!isErrorLine("Chapter:12: something"),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// ── bibtex ──────────────────────────────────────────────────────────────────────────────
|
|
172
|
+
const bibOut = [
|
|
173
|
+
"This is BibTeX, Version 0.99d (TeX Live 2023/Debian)",
|
|
174
|
+
"The top-level auxiliary file: paper.aux",
|
|
175
|
+
"The style file: plain.bst",
|
|
176
|
+
"I couldn't open database file nope.bib",
|
|
177
|
+
"---line 5 of file paper.aux",
|
|
178
|
+
" : \\bibdata{nope",
|
|
179
|
+
"(There were 2 error messages)",
|
|
180
|
+
].join("\n");
|
|
181
|
+
const bx = bibtexExcerpt(bibOut);
|
|
182
|
+
// Guards: filtering bibtex's fixed preamble — a failure would open with 'This is BibTeX, Version
|
|
183
|
+
// 0.99d' and push the real reason down.
|
|
184
|
+
check(
|
|
185
|
+
"bibtex: the banner is dropped, the explanation kept",
|
|
186
|
+
bx[0] === "I couldn't open database file nope.bib" &&
|
|
187
|
+
!bx.some((l) => l.startsWith("This is BibTeX")),
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// ── aux ─────────────────────────────────────────────────────────────────────────────────
|
|
191
|
+
const aux = [
|
|
192
|
+
"\\relax ",
|
|
193
|
+
"\\citation{knuth84}",
|
|
194
|
+
"\\citation{b,a}",
|
|
195
|
+
"\\bibstyle{plain}",
|
|
196
|
+
"\\bibdata{refs,more}",
|
|
197
|
+
"\\@input{chap1.aux}",
|
|
198
|
+
"\\newlabel{sec:intro}{{1}{1}{}{}{}}",
|
|
199
|
+
].join("\n");
|
|
200
|
+
const sub = "\\citation{c}\n\\citation{a}\n";
|
|
201
|
+
const parsed = auxBib(aux, (name) => (name === "chap1.aux" ? sub : null));
|
|
202
|
+
// Guards: citations from \include'd chapters — a \cite added in a chapter would not change the
|
|
203
|
+
// citation set, and bibtex would not rerun for it.
|
|
204
|
+
check(
|
|
205
|
+
"citations: sorted, unique, including the ones in an \\@input aux",
|
|
206
|
+
JSON.stringify(parsed.citations) ===
|
|
207
|
+
JSON.stringify(["a", "b", "c", "knuth84"]),
|
|
208
|
+
);
|
|
209
|
+
check(
|
|
210
|
+
"databases and style are read",
|
|
211
|
+
JSON.stringify(parsed.databases) === JSON.stringify(["refs", "more"]) &&
|
|
212
|
+
parsed.style === "plain",
|
|
213
|
+
);
|
|
214
|
+
check(
|
|
215
|
+
"an aux with no \\bibdata names no databases",
|
|
216
|
+
auxBib("\\relax\n\\citation{x}\n").databases.length === 0,
|
|
217
|
+
);
|
|
218
|
+
check(
|
|
219
|
+
"a self-including aux does not loop forever",
|
|
220
|
+
auxBib("\\@input{paper.aux}\n\\citation{x}", () => "\\@input{paper.aux}")
|
|
221
|
+
.citations.length === 1,
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
console.log(
|
|
225
|
+
`✓ ${String(n)} assertions passed — latex-log: markers across the 79-column wrap, both error spellings, aux`,
|
|
226
|
+
);
|
package/src/latex-log.ts
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading what pdflatex and bibtex leave behind — PURE functions, text in, data out.
|
|
3
|
+
*
|
|
4
|
+
* `latex-loop.ts` decides what to run next; this module turns the files a pass wrote into the
|
|
5
|
+
* facts that decision needs. Nothing here touches the disk: the caller reads the file and hands
|
|
6
|
+
* over its text, so every function below is testable on a string.
|
|
7
|
+
*
|
|
8
|
+
* 🔴 WHY LINE MATCHING IS LEGITIMATE HERE. A `.log` has no grammar a parser could give us: it is
|
|
9
|
+
* the terminal transcript of a TeX run. What it does have is a small set of DOCUMENTED marker
|
|
10
|
+
* lines that LaTeX and its packages print word for word, and that every build tool (latexmk,
|
|
11
|
+
* arara, texlab) keys on. Those markers are the interface. The `.aux` is similar: TeX writes one
|
|
12
|
+
* command per line, and the three we read (`\citation`, `\bibdata`, `\bibstyle`) always start a
|
|
13
|
+
* line. Neither file is prose-with-structure that a parser would read better.
|
|
14
|
+
*
|
|
15
|
+
* 🔴 THE 79-COLUMN WRAP. TeX breaks every line it writes to the log at `max_print_line` (79 in
|
|
16
|
+
* TeX Live), counting BYTES. A marker that happens to straddle the break — "Rerun to get
|
|
17
|
+
* cross-references right." is the usual victim, since it closes a long warning — is not on any
|
|
18
|
+
* one physical line. `unwrapLog` rejoins a line of exactly 79 bytes with the next one. The caller
|
|
19
|
+
* must therefore read the log as `latin1`, so one character is one byte and `length` counts what
|
|
20
|
+
* TeX counted.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** TeX Live's `max_print_line`. A physical log line of exactly this many bytes was broken by TeX. */
|
|
24
|
+
export const MAX_PRINT_LINE = 79;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Log lines that ask for another pass, or report on references. Each is a documented message,
|
|
28
|
+
* not a guess about wording:
|
|
29
|
+
* - `rerun-requested` — any "Rerun to get …" (LaTeX core, natbib, hyperref's outlines, …)
|
|
30
|
+
* - `labels-changed` — "LaTeX Warning: Label(s) may have changed."
|
|
31
|
+
* - `rerunfilecheck` — "Package rerunfilecheck Warning: File `…' has changed."
|
|
32
|
+
* - `undefined-references` / `undefined-citations` — the end-of-run summaries. These do NOT
|
|
33
|
+
* request a rerun by themselves: on a converged document they mean a genuinely undefined key.
|
|
34
|
+
*/
|
|
35
|
+
export type LogMarker =
|
|
36
|
+
| "rerun-requested"
|
|
37
|
+
| "labels-changed"
|
|
38
|
+
| "rerunfilecheck"
|
|
39
|
+
| "undefined-references"
|
|
40
|
+
| "undefined-citations";
|
|
41
|
+
|
|
42
|
+
/** Markers that mean "the output of this pass is not final yet". */
|
|
43
|
+
export const RERUN_MARKERS: readonly LogMarker[] = [
|
|
44
|
+
"rerun-requested",
|
|
45
|
+
"labels-changed",
|
|
46
|
+
"rerunfilecheck",
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Rejoin the lines TeX broke at column 79. A line of exactly 79 bytes continues on the next one.
|
|
51
|
+
*
|
|
52
|
+
* ⚠️ A line that is 79 bytes long BY ITSELF gets joined too — TeX gives no way to tell the two
|
|
53
|
+
* apart. The cost is one false join, which can only merge two lines, never split a marker; for
|
|
54
|
+
* the substring checks below that is harmless.
|
|
55
|
+
*/
|
|
56
|
+
export function unwrapLog(text: string): string[] {
|
|
57
|
+
const physical = text.split(/\r?\n/);
|
|
58
|
+
const out: string[] = [];
|
|
59
|
+
let carry = "";
|
|
60
|
+
for (const line of physical) {
|
|
61
|
+
if (line.length === MAX_PRINT_LINE) {
|
|
62
|
+
carry += line;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
out.push(carry + line);
|
|
66
|
+
carry = "";
|
|
67
|
+
}
|
|
68
|
+
if (carry) out.push(carry);
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Which markers a log carries, in a fixed order, each at most once. */
|
|
73
|
+
export function logMarkers(lines: readonly string[]): LogMarker[] {
|
|
74
|
+
const found = new Set<LogMarker>();
|
|
75
|
+
for (const line of lines) {
|
|
76
|
+
if (line.includes("Rerun to get")) found.add("rerun-requested");
|
|
77
|
+
if (line.startsWith("LaTeX Warning: Label(s) may have changed"))
|
|
78
|
+
found.add("labels-changed");
|
|
79
|
+
if (line.startsWith("Package rerunfilecheck Warning:"))
|
|
80
|
+
found.add("rerunfilecheck");
|
|
81
|
+
if (line.includes("There were undefined references"))
|
|
82
|
+
found.add("undefined-references");
|
|
83
|
+
if (line.includes("There were undefined citations"))
|
|
84
|
+
found.add("undefined-citations");
|
|
85
|
+
}
|
|
86
|
+
const order: LogMarker[] = [
|
|
87
|
+
"rerun-requested",
|
|
88
|
+
"labels-changed",
|
|
89
|
+
"rerunfilecheck",
|
|
90
|
+
"undefined-references",
|
|
91
|
+
"undefined-citations",
|
|
92
|
+
];
|
|
93
|
+
return order.filter((m) => found.has(m));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Is this the line a TeX error starts on? Two spellings exist and `-file-line-error` produces
|
|
98
|
+
* both: most errors become `./paper.tex:4: Undefined control sequence.`, while some keep the
|
|
99
|
+
* classic `! LaTeX Error: File `x.sty' not found.` (measured on TeX Live 2023). The path part must
|
|
100
|
+
* contain a dot or a slash, so an ordinary warning ending in a number and a colon cannot pass.
|
|
101
|
+
*/
|
|
102
|
+
export function isErrorLine(line: string): boolean {
|
|
103
|
+
if (line.startsWith("!")) return true;
|
|
104
|
+
const m = /^([^\s:]+):(\d+): /.exec(line);
|
|
105
|
+
return m !== null && /[./]/.test(m[1] ?? "");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The `l.NNN …` line TeX prints to show where in the source it stopped. */
|
|
109
|
+
export const isContextLine = (line: string): boolean => /^l\.\d+/.test(line);
|
|
110
|
+
|
|
111
|
+
/** At most this many lines are quoted from a log: enough for the error and its context. */
|
|
112
|
+
export const MAX_EXCERPT = 12;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The first error of a pass and the source context after it: the error line, the lines up to
|
|
116
|
+
* and including `l.NNN`, and the one line after it (TeX splits the source line at the point of
|
|
117
|
+
* the error, so the rest of it sits on the next line). Blank lines are dropped. Empty when the
|
|
118
|
+
* log carries no error line — the caller then says so rather than printing nothing.
|
|
119
|
+
*/
|
|
120
|
+
export function errorExcerpt(lines: readonly string[]): string[] {
|
|
121
|
+
const start = lines.findIndex(isErrorLine);
|
|
122
|
+
if (start < 0) return [];
|
|
123
|
+
const out: string[] = [];
|
|
124
|
+
for (let i = start; i < lines.length && out.length < MAX_EXCERPT; i++) {
|
|
125
|
+
const line = lines[i] ?? "";
|
|
126
|
+
if (line.trim() === "") continue;
|
|
127
|
+
out.push(line);
|
|
128
|
+
if (isContextLine(line)) {
|
|
129
|
+
const next = lines[i + 1];
|
|
130
|
+
if (next !== undefined && next.trim() !== "") out.push(next);
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** The lines bibtex prints before it has anything to say; never an explanation of a failure. */
|
|
138
|
+
const BIBTEX_PREAMBLE = [
|
|
139
|
+
"This is BibTeX",
|
|
140
|
+
"Capacity:",
|
|
141
|
+
"The top-level auxiliary file:",
|
|
142
|
+
"The style file:",
|
|
143
|
+
"Database file #",
|
|
144
|
+
];
|
|
145
|
+
|
|
146
|
+
/** What bibtex said beyond its banner — the explanation of a failed run. */
|
|
147
|
+
export function bibtexExcerpt(stdout: string): string[] {
|
|
148
|
+
return stdout
|
|
149
|
+
.split(/\r?\n/)
|
|
150
|
+
.filter(
|
|
151
|
+
(l) => l.trim() !== "" && !BIBTEX_PREAMBLE.some((p) => l.startsWith(p)),
|
|
152
|
+
)
|
|
153
|
+
.slice(0, MAX_EXCERPT);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** What the `.aux` tells bibtex: the cited keys, the databases and the style. */
|
|
157
|
+
export interface AuxBib {
|
|
158
|
+
/** Sorted, unique. `*` (`\nocite{*}`) is a key like any other. */
|
|
159
|
+
readonly citations: readonly string[];
|
|
160
|
+
/** The names in `\bibdata{…}`, as written — without `.bib`. */
|
|
161
|
+
readonly databases: readonly string[];
|
|
162
|
+
readonly style: string | null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** The argument of `\name{…}` when a line is exactly that command, else null. */
|
|
166
|
+
function argOf(line: string, name: string): string | null {
|
|
167
|
+
const open = `\\${name}{`;
|
|
168
|
+
if (!line.startsWith(open) || !line.endsWith("}")) return null;
|
|
169
|
+
return line.slice(open.length, -1);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const list = (arg: string): string[] =>
|
|
173
|
+
arg
|
|
174
|
+
.split(",")
|
|
175
|
+
.map((s) => s.trim())
|
|
176
|
+
.filter(Boolean);
|
|
177
|
+
|
|
178
|
+
/** What one walk over an `.aux` tree has gathered so far. Lines are read IN ORDER: see below. */
|
|
179
|
+
interface AuxAcc {
|
|
180
|
+
readonly citations: Set<string>;
|
|
181
|
+
readonly databases: string[];
|
|
182
|
+
style: string | null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** How a walk reaches nested aux files, and which it has already followed (a cycle ends there). */
|
|
186
|
+
interface AuxWalk {
|
|
187
|
+
readonly readInput: (name: string) => string | null;
|
|
188
|
+
readonly seen: Set<string>;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Fold one aux line into the walk. Order is load-bearing: a nested aux's databases land where
|
|
193
|
+
* its `\@input` stands, and its style counts only while no `\bibstyle` has been read yet — a
|
|
194
|
+
* later one in the parent still overrides it.
|
|
195
|
+
*/
|
|
196
|
+
function readAuxLine(line: string, acc: AuxAcc, walk: AuxWalk): void {
|
|
197
|
+
for (const k of list(argOf(line, "citation") ?? "")) acc.citations.add(k);
|
|
198
|
+
acc.databases.push(...list(argOf(line, "bibdata") ?? ""));
|
|
199
|
+
const st = argOf(line, "bibstyle");
|
|
200
|
+
if (st !== null) acc.style = st.trim();
|
|
201
|
+
followInput(argOf(line, "@input"), acc, walk);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Merge an `\@input` aux into the walk — once per name, and only when it exists. */
|
|
205
|
+
function followInput(input: string | null, acc: AuxAcc, walk: AuxWalk): void {
|
|
206
|
+
if (input === null || walk.seen.has(input)) return;
|
|
207
|
+
walk.seen.add(input);
|
|
208
|
+
const nested = walk.readInput(input);
|
|
209
|
+
if (nested === null) return;
|
|
210
|
+
const sub = auxBib(nested, walk.readInput, walk.seen);
|
|
211
|
+
for (const k of sub.citations) acc.citations.add(k);
|
|
212
|
+
acc.databases.push(...sub.databases);
|
|
213
|
+
acc.style ??= sub.style;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Read `\citation`, `\bibdata` and `\bibstyle` out of an `.aux`, following `\@input{sub.aux}` —
|
|
218
|
+
* `\include` writes each chapter's citations into its own aux file, and bibtex follows them too.
|
|
219
|
+
* `readInput` returns the text of a nested aux, or null when it does not exist.
|
|
220
|
+
*/
|
|
221
|
+
export function auxBib(
|
|
222
|
+
text: string,
|
|
223
|
+
readInput: (name: string) => string | null = () => null,
|
|
224
|
+
seen: Set<string> = new Set(),
|
|
225
|
+
): AuxBib {
|
|
226
|
+
const acc: AuxAcc = { citations: new Set(), databases: [], style: null };
|
|
227
|
+
for (const raw of text.split(/\r?\n/))
|
|
228
|
+
readAuxLine(raw.trim(), acc, { readInput, seen });
|
|
229
|
+
return {
|
|
230
|
+
citations: [...acc.citations].sort(),
|
|
231
|
+
databases: acc.databases,
|
|
232
|
+
style: acc.style,
|
|
233
|
+
};
|
|
234
|
+
}
|