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.
- package/.github/workflows/ci.yml +12 -7
- package/CLAUDE.md +5 -5
- package/CONTRIBUTING.md +34 -5
- package/dist/adapters/banal/failure.d.ts +1 -7
- package/dist/adapters/banal/failure.d.ts.map +1 -1
- package/dist/adapters/banal/failure.js +5 -7
- package/dist/adapters/banal/failure.js.map +1 -1
- package/dist/adapters/banal/index.d.ts.map +1 -1
- package/dist/adapters/banal/index.js +4 -3
- package/dist/adapters/banal/index.js.map +1 -1
- package/dist/adapters/banal/install.d.ts.map +1 -1
- package/dist/adapters/banal/install.js +4 -3
- package/dist/adapters/banal/install.js.map +1 -1
- package/dist/adapters/banal/invocation.d.ts +10 -5
- package/dist/adapters/banal/invocation.d.ts.map +1 -1
- package/dist/adapters/banal/invocation.js +13 -1
- package/dist/adapters/banal/invocation.js.map +1 -1
- package/dist/adapters/banal/locate.d.ts +12 -9
- package/dist/adapters/banal/locate.d.ts.map +1 -1
- package/dist/adapters/banal/locate.js +19 -12
- package/dist/adapters/banal/locate.js.map +1 -1
- package/dist/adapters/banal/run.d.ts +10 -1
- package/dist/adapters/banal/run.d.ts.map +1 -1
- package/dist/adapters/banal/run.js +8 -4
- package/dist/adapters/banal/run.js.map +1 -1
- package/dist/adapters/banal/settings.d.ts +1 -1
- package/dist/adapters/banal/settings.d.ts.map +1 -1
- package/dist/adapters/banal/settings.js +2 -1
- package/dist/adapters/banal/settings.js.map +1 -1
- package/dist/adapters/memory/index.d.ts.map +1 -1
- package/dist/adapters/memory/index.js +8 -2
- package/dist/adapters/memory/index.js.map +1 -1
- package/dist/adapters/node/files.io.d.ts.map +1 -1
- package/dist/adapters/node/files.io.js +2 -1
- package/dist/adapters/node/files.io.js.map +1 -1
- package/dist/adapters/node/process.io.d.ts +5 -7
- package/dist/adapters/node/process.io.d.ts.map +1 -1
- package/dist/adapters/node/process.io.js +4 -3
- package/dist/adapters/node/process.io.js.map +1 -1
- package/dist/adapters/node/workspace.io.d.ts.map +1 -1
- package/dist/adapters/node/workspace.io.js +4 -2
- package/dist/adapters/node/workspace.io.js.map +1 -1
- package/dist/adapters/pdfjs/fill.d.ts.map +1 -1
- package/dist/adapters/pdfjs/fill.js +13 -7
- package/dist/adapters/pdfjs/fill.js.map +1 -1
- package/dist/adapters/references/index.d.ts.map +1 -1
- package/dist/adapters/references/index.js +8 -7
- package/dist/adapters/references/index.js.map +1 -1
- package/dist/adapters/references/pool.d.ts.map +1 -1
- package/dist/adapters/references/pool.js +4 -5
- package/dist/adapters/references/pool.js.map +1 -1
- package/dist/adapters/references/reach.io.d.ts.map +1 -1
- package/dist/adapters/references/reach.io.js +2 -1
- package/dist/adapters/references/reach.io.js.map +1 -1
- package/dist/build-engine.d.ts +1 -1
- package/dist/build-engine.d.ts.map +1 -1
- package/dist/build-engine.js +10 -3
- package/dist/build-engine.js.map +1 -1
- package/dist/build.d.ts +2 -3
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +19 -17
- package/dist/build.js.map +1 -1
- package/dist/caller-path.d.ts +3 -0
- package/dist/caller-path.d.ts.map +1 -0
- package/dist/caller-path.js +13 -0
- package/dist/caller-path.js.map +1 -0
- package/dist/cli.d.ts +10 -17
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +93 -46
- package/dist/cli.js.map +1 -1
- package/dist/doctor.d.ts +12 -3
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +5 -3
- package/dist/doctor.js.map +1 -1
- package/dist/domain/lookup-cache.d.ts.map +1 -1
- package/dist/domain/lookup-cache.js +10 -8
- package/dist/domain/lookup-cache.js.map +1 -1
- package/dist/domain/paths.d.ts +8 -2
- package/dist/domain/paths.d.ts.map +1 -1
- package/dist/domain/paths.js +12 -1
- package/dist/domain/paths.js.map +1 -1
- package/dist/domain/ready.d.ts +24 -0
- package/dist/domain/ready.d.ts.map +1 -0
- package/dist/domain/ready.js +11 -0
- package/dist/domain/ready.js.map +1 -0
- package/dist/domain/record.d.ts +8 -0
- package/dist/domain/record.d.ts.map +1 -0
- package/dist/domain/record.js +8 -0
- package/dist/domain/record.js.map +1 -0
- package/dist/domain/sha256.d.ts.map +1 -1
- package/dist/domain/sha256.js +4 -2
- package/dist/domain/sha256.js.map +1 -1
- package/dist/domain/text.d.ts +12 -0
- package/dist/domain/text.d.ts.map +1 -1
- package/dist/domain/text.js +17 -0
- package/dist/domain/text.js.map +1 -1
- package/dist/engine.d.ts +23 -22
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +4 -3
- package/dist/engine.js.map +1 -1
- package/dist/eslint-rules/bib-reachable-entry.d.ts +22 -0
- package/dist/eslint-rules/bib-reachable-entry.d.ts.map +1 -0
- package/dist/eslint-rules/bib-reachable-entry.js +75 -0
- package/dist/eslint-rules/bib-reachable-entry.js.map +1 -0
- package/dist/eslint-rules/install-path-literals.d.ts +80 -0
- package/dist/eslint-rules/install-path-literals.d.ts.map +1 -0
- package/dist/eslint-rules/install-path-literals.js +105 -0
- package/dist/eslint-rules/install-path-literals.js.map +1 -0
- package/dist/eslint-rules/latex-language.d.ts +136 -0
- package/dist/eslint-rules/latex-language.d.ts.map +1 -0
- package/dist/eslint-rules/latex-language.js +639 -0
- package/dist/eslint-rules/latex-language.js.map +1 -0
- package/dist/eslint-rules/paper-research-question.d.ts +31 -0
- package/dist/eslint-rules/paper-research-question.d.ts.map +1 -0
- package/dist/eslint-rules/paper-research-question.js +188 -0
- package/dist/eslint-rules/paper-research-question.js.map +1 -0
- package/dist/eslint-rules/paper-stages.d.ts +83 -0
- package/dist/eslint-rules/paper-stages.d.ts.map +1 -0
- package/dist/eslint-rules/paper-stages.js +358 -0
- package/dist/eslint-rules/paper-stages.js.map +1 -0
- package/dist/eslint-rules/paper-typography.d.ts +74 -0
- package/dist/eslint-rules/paper-typography.d.ts.map +1 -0
- package/dist/eslint-rules/paper-typography.js +404 -0
- package/dist/eslint-rules/paper-typography.js.map +1 -0
- package/dist/eslint-rules/papers.d.ts +42 -0
- package/dist/eslint-rules/papers.d.ts.map +1 -0
- package/dist/eslint-rules/papers.js +145 -0
- package/dist/eslint-rules/papers.js.map +1 -0
- package/dist/eslint-rules/pdf-last-page-balance.d.ts +72 -0
- package/dist/eslint-rules/pdf-last-page-balance.d.ts.map +1 -0
- package/dist/eslint-rules/pdf-last-page-balance.js +237 -0
- package/dist/eslint-rules/pdf-last-page-balance.js.map +1 -0
- package/dist/eslint-rules/review-frontmatter.d.ts +57 -0
- package/dist/eslint-rules/review-frontmatter.d.ts.map +1 -0
- package/dist/eslint-rules/review-frontmatter.js +126 -0
- package/dist/eslint-rules/review-frontmatter.js.map +1 -0
- package/dist/eslint-rules/review-frontmatter.schema.json +36 -0
- package/dist/eslint-rules/rule-context.d.ts +55 -0
- package/dist/eslint-rules/rule-context.d.ts.map +1 -0
- package/dist/eslint-rules/rule-context.js +9 -0
- package/dist/eslint-rules/rule-context.js.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.d.ts +25 -0
- package/dist/eslint-rules/sibling-frontmatter.d.ts.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.js +16 -0
- package/dist/eslint-rules/sibling-frontmatter.js.map +1 -0
- package/dist/eslint-rules/sibling-frontmatter.schema.json +14 -0
- package/dist/eslint-rules/temp-root-realpath.d.ts +71 -0
- package/dist/eslint-rules/temp-root-realpath.d.ts.map +1 -0
- package/dist/eslint-rules/temp-root-realpath.js +139 -0
- package/dist/eslint-rules/temp-root-realpath.js.map +1 -0
- package/dist/eslint-rules/tex-build.d.ts +35 -0
- package/dist/eslint-rules/tex-build.d.ts.map +1 -0
- package/dist/eslint-rules/tex-build.js +313 -0
- package/dist/eslint-rules/tex-build.js.map +1 -0
- package/dist/facts-file.d.ts.map +1 -1
- package/dist/facts-file.js +31 -27
- package/dist/facts-file.js.map +1 -1
- package/dist/hooks-settings.d.ts +1 -1
- package/dist/hooks-settings.d.ts.map +1 -1
- package/dist/hooks-settings.js +31 -11
- package/dist/hooks-settings.js.map +1 -1
- package/dist/init.d.ts +8 -9
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +33 -15
- package/dist/init.js.map +1 -1
- package/dist/latex-loop.d.ts +2 -2
- package/dist/latex-loop.d.ts.map +1 -1
- package/dist/latex-loop.js +2 -2
- package/dist/latex-loop.js.map +1 -1
- package/dist/lib/agent-cli-version.d.ts +65 -0
- package/dist/lib/agent-cli-version.d.ts.map +1 -0
- package/dist/lib/agent-cli-version.js +77 -0
- package/dist/lib/agent-cli-version.js.map +1 -0
- package/dist/lib/check.d.ts +19 -0
- package/dist/lib/check.d.ts.map +1 -0
- package/dist/lib/check.js +52 -0
- package/dist/lib/check.js.map +1 -0
- package/dist/lib/markdown.d.ts +180 -0
- package/dist/lib/markdown.d.ts.map +1 -0
- package/dist/lib/markdown.js +302 -0
- package/dist/lib/markdown.js.map +1 -0
- package/dist/lib/paper-config.d.ts +84 -0
- package/dist/lib/paper-config.d.ts.map +1 -0
- package/dist/lib/paper-config.js +138 -0
- package/dist/lib/paper-config.js.map +1 -0
- package/dist/lib/skill-eval-fixture.d.ts +29 -0
- package/dist/lib/skill-eval-fixture.d.ts.map +1 -0
- package/dist/lib/skill-eval-fixture.js +62 -0
- package/dist/lib/skill-eval-fixture.js.map +1 -0
- package/dist/link-skills.d.ts.map +1 -1
- package/dist/link-skills.js +5 -4
- package/dist/link-skills.js.map +1 -1
- package/dist/new-paper.d.ts.map +1 -1
- package/dist/new-paper.js +7 -3
- package/dist/new-paper.js.map +1 -1
- package/dist/paper-files.d.ts +4 -4
- package/dist/paper-files.d.ts.map +1 -1
- package/dist/paper-files.js +6 -6
- package/dist/paper-files.js.map +1 -1
- package/dist/paper-settings.d.ts.map +1 -1
- package/dist/paper-settings.js +8 -7
- package/dist/paper-settings.js.map +1 -1
- package/dist/pdf-facts.d.ts +51 -1
- package/dist/pdf-facts.d.ts.map +1 -1
- package/dist/pdf-facts.js +25 -16
- package/dist/pdf-facts.js.map +1 -1
- package/dist/pdf-geometry.d.ts.map +1 -1
- package/dist/pdf-geometry.js +3 -4
- package/dist/pdf-geometry.js.map +1 -1
- package/dist/ports/tool-installer.d.ts +2 -14
- package/dist/ports/tool-installer.d.ts.map +1 -1
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +19 -6
- package/dist/presets.js.map +1 -1
- package/dist/references.d.ts.map +1 -1
- package/dist/references.js +15 -10
- package/dist/references.js.map +1 -1
- package/dist/rules-config.d.ts +26 -1
- package/dist/rules-config.d.ts.map +1 -1
- package/dist/rules-config.js +15 -12
- package/dist/rules-config.js.map +1 -1
- package/dist/structure.js +1 -1
- package/dist/structure.js.map +1 -1
- package/dist/tex-requirements.d.ts +4 -0
- package/dist/tex-requirements.d.ts.map +1 -1
- package/dist/tex-requirements.js +22 -5
- package/dist/tex-requirements.js.map +1 -1
- package/dist/toolchain.d.ts.map +1 -1
- package/dist/toolchain.js +21 -16
- package/dist/toolchain.js.map +1 -1
- package/dist/types.d.ts +5 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/venue-rules.d.ts.map +1 -1
- package/dist/venue-rules.js +21 -8
- package/dist/venue-rules.js.map +1 -1
- package/docs/configuration.md +2 -2
- package/docs/e2e.md +7 -7
- package/docs/install.md +1 -1
- package/docs/prior-art/README.md +1 -1
- package/docs/prior-art/package-location.md +5 -5
- package/docs/prior-art/repro/README.md +1 -1
- package/docs/prior-art/test-tooling.md +2 -2
- package/docs/testing.md +11 -8
- package/docs/toolchain.md +1 -1
- package/eslint-rules/{bib-reachable-entry.mjs → bib-reachable-entry.ts} +5 -4
- package/eslint-rules/install-path-literals.harness.mjs +1 -1
- package/eslint-rules/{install-path-literals.mjs → install-path-literals.ts} +18 -10
- package/eslint-rules/latex-language.harness.mjs +2 -2
- package/eslint-rules/latex-language.test.mjs +7 -1
- package/eslint-rules/{latex-language.mjs → latex-language.ts} +297 -109
- package/eslint-rules/paper-research-question.harness.mjs +3 -3
- package/eslint-rules/{paper-research-question.mjs → paper-research-question.ts} +35 -16
- package/eslint-rules/paper-stages.harness.mjs +2 -2
- package/eslint-rules/paper-stages.test.mjs +8 -2
- package/eslint-rules/{paper-stages.mjs → paper-stages.ts} +76 -31
- package/eslint-rules/paper-typography.test.mjs +3 -3
- package/eslint-rules/{paper-typography.mjs → paper-typography.ts} +134 -56
- package/eslint-rules/papers.harness.mjs +2 -2
- package/eslint-rules/{papers.mjs → papers.ts} +7 -4
- package/eslint-rules/pdf-last-page-balance.harness.mjs +3 -3
- package/eslint-rules/{pdf-last-page-balance.mjs → pdf-last-page-balance.ts} +89 -32
- package/eslint-rules/review-frontmatter.test.mjs +2 -2
- package/eslint-rules/{review-frontmatter.mjs → review-frontmatter.ts} +38 -16
- package/eslint-rules/rule-context.ts +62 -0
- package/eslint-rules/{sibling-frontmatter.mjs → sibling-frontmatter.ts} +1 -1
- package/eslint-rules/temp-root-realpath.harness.mjs +1 -1
- package/eslint-rules/{temp-root-realpath.mjs → temp-root-realpath.ts} +20 -7
- package/eslint-rules/tex-build.harness.mjs +2 -2
- package/eslint-rules/{tex-build.mjs → tex-build.ts} +6 -5
- package/eslint.config.mjs +146 -30
- package/fixtures/install-path-literals/clean.fixture.mjs +1 -1
- package/fixtures/install-path-literals/clean.md +2 -2
- package/fixtures/install-path-literals/defect.fixture.mjs +1 -1
- package/fixtures/install-path-literals/defect.md +2 -2
- package/fixtures/pdf-facts/README.md +2 -2
- package/fixtures/real-markdown-paper/{baseline.mjs → baseline.ts} +31 -10
- package/hooks/{hooks.harness.mjs → hooks.harness.ts} +51 -34
- package/hooks/paper-edit-guard.hook.mjs +1 -1
- package/hooks/{paper-edit-guard.test.mjs → paper-edit-guard.test.ts} +3 -7
- package/hooks/paper-skills-nudge.hook.mjs +1 -1
- package/hooks/{paper-skills-nudge.test.mjs → paper-skills-nudge.test.ts} +2 -6
- package/hooks/paper-status-gates.sh +9 -19
- package/hooks/{paper-status-gates.test.mjs → paper-status-gates.test.ts} +2 -6
- package/lib/agent-cli-version.harness.mjs +1 -1
- package/lib/{agent-cli-version.mjs → agent-cli-version.ts} +32 -18
- package/lib/check.test.ts +14 -9
- package/lib/{check.mjs → check.ts} +22 -9
- package/lib/markdown.test.mjs +7 -7
- package/lib/{markdown.mjs → markdown.ts} +68 -29
- package/lib/paper-config.harness.mjs +9 -9
- package/lib/{paper-config.mjs → paper-config.ts} +20 -19
- package/lib/skill-checks.mjs +24 -381
- package/lib/skill-checks.test.mjs +64 -116
- package/lib/skill-corpus.mjs +10 -251
- package/lib/skill-corpus.test.mjs +2 -13
- package/lib/skill-eval-kit.mjs +1 -1
- package/lib/skill-trigger-cases.mjs +1 -1
- package/package.json +42 -10
- package/scripts/{check.harness.mjs → check.harness.ts} +111 -10
- package/scripts/{check.test.mjs → check.test.ts} +13 -10
- package/scripts/{check.mjs → check.ts} +116 -42
- package/scripts/coverage-config.test.ts +80 -38
- package/scripts/eslint-report-guard.test.mjs +1 -1
- package/scripts/harness-api.frozen.json +14 -16
- package/scripts/harness-api.test.ts +8 -5
- package/scripts/layer-legacy-frozen.test.ts +17 -13
- package/scripts/{layer-legacy-frozen.mjs → layer-legacy-frozen.ts} +128 -39
- package/scripts/release-config.test.ts +12 -4
- package/scripts/rules-see-files.test.mjs +1 -1
- package/scripts/test.test.ts +78 -0
- package/scripts/test.ts +87 -0
- package/skills/analyze-sibling-paper/SKILL.md +1 -39
- package/skills/analyze-sibling-paper/SKILL.md.spec.ts +0 -38
- package/skills/argument-arc/SKILL.md +2 -36
- package/skills/argument-arc/SKILL.md.spec.ts +1 -43
- package/skills/build-benchmark/SKILL.md +1 -41
- package/skills/build-benchmark/SKILL.md.spec.ts +0 -40
- package/skills/camera-ready/SKILL.md +1 -34
- package/skills/camera-ready/SKILL.md.spec.ts +0 -33
- package/skills/cold-read-diff/SKILL.md +1 -35
- package/skills/cold-read-diff/SKILL.md.spec.ts +0 -34
- package/skills/draft-paper/SKILL.md +2 -41
- package/skills/draft-paper/SKILL.md.spec.ts +1 -50
- package/skills/extend-paper/SKILL.md +2 -33
- package/skills/extend-paper/SKILL.md.spec.ts +1 -42
- package/skills/find-venue/SKILL.md +2 -38
- package/skills/find-venue/SKILL.md.spec.ts +1 -47
- package/skills/grade-paper-writing/SKILL.md +3 -38
- package/skills/grade-paper-writing/SKILL.md.spec.ts +2 -47
- package/skills/grade-paper-writing/prose-lint.mjs +3 -4
- package/skills/grade-paper-writing/{prose-lint.test.mjs → prose-lint.test.ts} +3 -3
- package/skills/harden-paper/SKILL.md +1 -39
- package/skills/harden-paper/SKILL.md.spec.ts +0 -38
- package/skills/map-prior-work/SKILL.md +1 -35
- package/skills/map-prior-work/SKILL.md.spec.ts +0 -34
- package/skills/osf-artifact-upload/{osf-artifact-upload.harness.mjs → osf-artifact-upload.harness.ts} +3 -4
- package/skills/paper-adversarial-review/SKILL.md +2 -35
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +1 -43
- package/skills/paper-pipeline/SKILL.md +2 -28
- package/skills/paper-pipeline/SKILL.md.spec.ts +1 -27
- package/skills/paper-pipeline/description-language.eval.mjs +1 -1
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +1 -1
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +2 -2
- package/skills/paper-pipeline/paper-pipeline.harness.mjs +25 -40
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +1 -1
- package/skills/paper-pipeline/pipeline-language.eval.mjs +1 -1
- package/skills/paper-pipeline/references/acceptance-gate.md +1 -6
- package/skills/paper-pipeline/references/pipeline-status-template.md +8 -35
- package/skills/paper-pipeline/scripts/README.md +30 -225
- package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +4 -8
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +1 -1
- package/skills/paper-pipeline/scripts/{artifact-coverage.test.mjs → artifact-coverage.test.ts} +2 -2
- package/skills/paper-pipeline/scripts/consumer.harness.mjs +6 -104
- package/skills/paper-pipeline/scripts/consumer.mjs +24 -109
- package/skills/paper-pipeline/scripts/consumer.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +1 -1
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +1 -4
- package/skills/paper-pipeline/scripts/extract-ref-facts.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +2 -3
- package/skills/paper-pipeline/scripts/{generated-code.test.mjs → generated-code.test.ts} +3 -3
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +3 -3
- package/skills/paper-pipeline/scripts/population-map.mjs +1 -4
- package/skills/paper-pipeline/scripts/population-map.test.mjs +1 -1
- package/skills/paper-pipeline/scripts/{round-diff.harness.mjs → round-diff.harness.ts} +45 -18
- package/skills/paper-pipeline/scripts/round-diff.mjs +1 -1
- package/skills/paper-pipeline/scripts/round-diff.test.mjs +1 -1
- package/skills/paper-status/SKILL.md +2 -42
- package/skills/paper-status/SKILL.md.spec.ts +1 -41
- package/skills/pc-panel-review/SKILL.md +2 -46
- package/skills/pc-panel-review/SKILL.md.spec.ts +1 -45
- package/skills/plan-paper-timeline/SKILL.md +2 -36
- package/skills/plan-paper-timeline/SKILL.md.spec.ts +0 -36
- package/skills/plan-paper-timeline/fixtures/{fake-google-calendar.mjs → fake-google-calendar.ts} +101 -70
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +3 -3
- package/skills/render-paper/SKILL.md +1 -37
- package/skills/render-paper/SKILL.md.spec.ts +0 -36
- package/skills/render-paper/{extract-pdf-facts.harness.mjs → extract-pdf-facts.harness.ts} +34 -9
- package/skills/render-paper/render-paper.harness.mjs +2 -2
- package/skills/research-ideate/SKILL.md +2 -39
- package/skills/research-ideate/SKILL.md.spec.ts +1 -47
- package/skills/study-accepted-papers/SKILL.md +1 -34
- package/skills/study-accepted-papers/SKILL.md.spec.ts +0 -33
- package/skills/submit-paper/SKILL.md +1 -33
- package/skills/submit-paper/SKILL.md.spec.ts +0 -32
- package/skills/sweep-design-space/SKILL.md +1 -38
- package/skills/sweep-design-space/SKILL.md.spec.ts +0 -37
- package/skills/tighten-paper/SKILL.md +2 -39
- package/skills/tighten-paper/SKILL.md.spec.ts +1 -47
- package/skills/tighten-paper/structure.mjs +5 -12
- package/skills/tighten-paper/{structure.test.mjs → structure.test.ts} +4 -3
- package/skills/verify-citations/SKILL.md +2 -38
- package/skills/verify-citations/SKILL.md.spec.ts +1 -37
- package/skills/verify-citations/scripts/{bib-authors.cli.test.mjs → bib-authors.cli.test.ts} +14 -4
- package/skills/verify-citations/scripts/bib-authors.d.mts +63 -0
- package/skills/verify-citations/scripts/bib-authors.mjs +1 -4
- package/skills/verify-citations/scripts/{verify-cites.cli.test.mjs → verify-cites.cli.test.ts} +32 -9
- package/skills/verify-citations/scripts/verify-cites.d.mts +58 -0
- package/src/CLAUDE.md +1 -1
- package/src/adapters/banal/failure.ts +9 -10
- package/src/adapters/banal/index.test.ts +5 -8
- package/src/adapters/banal/index.ts +8 -7
- package/src/adapters/banal/install.ts +9 -4
- package/src/adapters/banal/invocation.test.ts +23 -9
- package/src/adapters/banal/invocation.ts +24 -9
- package/src/adapters/banal/locate.test.ts +2 -2
- package/src/adapters/banal/locate.ts +27 -21
- package/src/adapters/banal/output.test.ts +1 -1
- package/src/adapters/banal/run.test.ts +5 -5
- package/src/adapters/banal/run.ts +16 -10
- package/src/adapters/banal/settings.ts +2 -2
- package/src/adapters/banal/xml.test.ts +1 -1
- package/src/adapters/curl/download.test.ts +8 -6
- package/src/adapters/memory/index.ts +4 -3
- package/src/adapters/node/files.io.ts +2 -2
- package/src/adapters/node/files.test.ts +8 -4
- package/src/adapters/node/process.io.ts +13 -5
- package/src/adapters/node/process.test.ts +5 -5
- package/src/adapters/node/workspace.io.ts +9 -4
- package/src/adapters/node/workspace.test.ts +3 -1
- package/src/adapters/pdfjs/fill.test.ts +2 -2
- package/src/adapters/pdfjs/fill.ts +14 -7
- package/src/adapters/references/index.test.ts +31 -38
- package/src/adapters/references/index.ts +33 -60
- package/src/adapters/references/pool.ts +3 -5
- package/src/adapters/references/reach.io.ts +2 -1
- package/src/build-engine.harness.mjs +1 -1
- package/src/build-engine.test.ts +13 -8
- package/src/build-engine.ts +12 -7
- package/src/build.harness.mjs +2 -2
- package/src/build.test.ts +24 -24
- package/src/build.ts +34 -26
- package/src/caller-path.ts +13 -0
- package/src/cli.harness.mjs +6 -8
- package/src/cli.test.ts +31 -13
- package/src/cli.ts +117 -58
- package/src/doctor.harness.mjs +3 -3
- package/src/doctor.test.ts +1 -1
- package/src/doctor.ts +17 -7
- package/src/domain/lookup-cache.ts +21 -11
- package/src/domain/paths.test.ts +15 -0
- package/src/domain/paths.ts +20 -1
- package/src/domain/ready.ts +31 -0
- package/src/domain/record.test.ts +20 -0
- package/src/domain/record.ts +12 -0
- package/src/domain/result.test.ts +2 -2
- package/src/domain/sha256.ts +5 -3
- package/src/domain/text.test.ts +32 -0
- package/src/domain/text.ts +23 -0
- package/src/engine.harness.mjs +2 -2
- package/src/engine.ts +34 -5
- package/src/facts-file.test.ts +9 -7
- package/src/facts-file.ts +45 -29
- package/src/{hooks-settings.harness.mjs → hooks-settings.harness.ts} +56 -20
- package/src/hooks-settings.ts +48 -13
- package/src/init.test.ts +37 -14
- package/src/init.ts +48 -24
- package/src/{latex-log.harness.mjs → latex-log.harness.ts} +14 -14
- package/src/{latex-loop.harness.mjs → latex-loop.harness.ts} +64 -36
- package/src/latex-loop.ts +3 -3
- package/src/{link-skills.harness.mjs → link-skills.harness.ts} +19 -19
- package/src/link-skills.test.ts +3 -3
- package/src/link-skills.ts +5 -4
- package/src/lint-fix.test.ts +4 -6
- package/src/linted-set.test.ts +2 -2
- package/src/new-paper.harness.mjs +9 -25
- package/src/new-paper.test.ts +8 -10
- package/src/new-paper.ts +9 -5
- package/src/paper-files.test.ts +25 -24
- package/src/paper-files.ts +16 -27
- package/src/paper-settings-commands.test.ts +5 -4
- package/src/paper-settings.test.ts +7 -7
- package/src/paper-settings.ts +13 -14
- package/src/{pdf-facts.harness.mjs → pdf-facts.harness.ts} +9 -8
- package/src/pdf-facts.test.ts +25 -15
- package/src/pdf-facts.ts +81 -23
- package/src/{pdf-geometry.harness.mjs → pdf-geometry.harness.ts} +19 -12
- package/src/pdf-geometry.ts +8 -4
- package/src/ports/tool-installer.ts +2 -17
- package/src/presets.ts +31 -16
- package/src/references.test.ts +76 -62
- package/src/references.ts +18 -12
- package/src/{rules-config.harness.mjs → rules-config.harness.ts} +7 -13
- package/src/rules-config.ts +46 -14
- package/src/rules-off.test.ts +6 -4
- package/src/{structure.harness.mjs → structure.harness.ts} +26 -15
- package/src/structure.ts +1 -1
- package/src/{tex-requirements.harness.mjs → tex-requirements.harness.ts} +50 -22
- package/src/tex-requirements.test.ts +21 -0
- package/src/tex-requirements.ts +37 -11
- package/src/toolchain.harness.mjs +2 -2
- package/src/toolchain.test.ts +10 -6
- package/src/toolchain.ts +31 -17
- package/src/types.ts +5 -3
- package/src/venue-rules.test.ts +70 -68
- package/src/venue-rules.ts +22 -12
- package/tsconfig.json +6 -3
- package/tsconfig.pkg.json +10 -0
- package/lib/check.d.mts +0 -17
- package/lib/paper-config.d.mts +0 -31
- package/scripts/layer-legacy-frozen.d.mts +0 -36
- package/scripts/rules-are-content-only.harness.mjs +0 -105
- package/scripts/rules-are-content-only.mjs +0 -148
- package/scripts/rules-are-content-only.test.mjs +0 -61
- package/scripts/test.mjs +0 -44
- package/scripts/test.test.mjs +0 -52
- package/skills/paper-pipeline/scripts/announce.mjs +0 -67
- package/skills/paper-pipeline/scripts/announce.test.mjs +0 -102
- package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +0 -51
- package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +0 -52
- package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +0 -6
- package/skills/paper-pipeline/scripts/fixtures/scorecard.mjs +0 -71
- package/skills/paper-pipeline/scripts/ledger.cli.test.mjs +0 -302
- package/skills/paper-pipeline/scripts/ledger.mjs +0 -623
- package/skills/paper-pipeline/scripts/ledger.test.mjs +0 -254
- package/skills/paper-pipeline/scripts/pipeline-check.cli.test.mjs +0 -474
- package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +0 -328
- package/skills/paper-pipeline/scripts/pipeline-check.mjs +0 -753
- package/skills/paper-pipeline/scripts/pipeline-edges.mjs +0 -169
- package/skills/paper-pipeline/scripts/pipeline-edges.test.mjs +0 -73
- package/skills/paper-pipeline/scripts/run-mechanical.mjs +0 -648
- package/skills/paper-pipeline/scripts/run-mechanical.test.mjs +0 -468
- package/skills/paper-pipeline/scripts/status.mjs +0 -295
- package/skills/paper-pipeline/scripts/status.test.mjs +0 -232
- /package/bin/{paperlint.test.mjs → paperlint.test.ts} +0 -0
- /package/lib/{skill-eval-fixture.mjs → skill-eval-fixture.ts} +0 -0
- /package/skills/grade-paper-writing/__snapshots__/{prose-lint.test.mjs.snap → prose-lint.test.ts.snap} +0 -0
- /package/skills/tighten-paper/__snapshots__/{structure.test.mjs.snap → structure.test.ts.snap} +0 -0
package/src/init.test.ts
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
reportTexLive,
|
|
35
35
|
reportWorkflow,
|
|
36
36
|
} from "./init.ts";
|
|
37
|
-
import { runNode } from "../test/support.
|
|
37
|
+
import { runNode } from "../test/support.ts";
|
|
38
38
|
|
|
39
39
|
const dirs: string[] = [];
|
|
40
40
|
afterEach(() => {
|
|
@@ -70,7 +70,7 @@ describe("paperlint init — a declared papers directory", () => {
|
|
|
70
70
|
err: (s: string) => out.push(s),
|
|
71
71
|
interactive: false,
|
|
72
72
|
hooks: false,
|
|
73
|
-
run: (
|
|
73
|
+
run: () => ({ status: 0 }),
|
|
74
74
|
link: () => ({ ok: false, error: "not linked in this test" }),
|
|
75
75
|
});
|
|
76
76
|
const text = out.join("\n");
|
|
@@ -81,7 +81,9 @@ describe("paperlint init — a declared papers directory", () => {
|
|
|
81
81
|
expect(code).toBe(0);
|
|
82
82
|
expect(section).toMatch(/✓ papers — declared in paperlint\.json/);
|
|
83
83
|
expect(section).not.toMatch(/candidates|eslint-rules|drafts/);
|
|
84
|
-
const cfg = JSON.parse(
|
|
84
|
+
const cfg: unknown = JSON.parse(
|
|
85
|
+
readFileSync(join(root, "paperlint.json"), "utf8"),
|
|
86
|
+
);
|
|
85
87
|
expect(cfg).toEqual({ papersDir: "papers" });
|
|
86
88
|
});
|
|
87
89
|
|
|
@@ -93,9 +95,9 @@ describe("paperlint init — a declared papers directory", () => {
|
|
|
93
95
|
log: () => {},
|
|
94
96
|
err: () => {},
|
|
95
97
|
interactive: true,
|
|
96
|
-
ask:
|
|
98
|
+
ask: (q) => Promise.resolve((asked.push(q), "n")),
|
|
97
99
|
hooks: false,
|
|
98
|
-
run: (
|
|
100
|
+
run: () => ({ status: 0 }),
|
|
99
101
|
link: () => ({ ok: false, error: "not linked in this test" }),
|
|
100
102
|
});
|
|
101
103
|
expect(asked.filter((q) => /papers roots/.test(q))).toEqual([]);
|
|
@@ -114,7 +116,7 @@ describe("paperlint init — writes paperlint.json only when something differs f
|
|
|
114
116
|
err: () => {},
|
|
115
117
|
interactive: false,
|
|
116
118
|
hooks: false,
|
|
117
|
-
run: (
|
|
119
|
+
run: () => ({ status: 0 }),
|
|
118
120
|
link: () => ({ ok: false as const, error: "not linked in this test" }),
|
|
119
121
|
};
|
|
120
122
|
const fresh = (files: Record<string, string>): string => {
|
|
@@ -165,9 +167,10 @@ const texRun = async (
|
|
|
165
167
|
log: (s: string) => out.push(s),
|
|
166
168
|
err: () => {},
|
|
167
169
|
interactive,
|
|
168
|
-
ask:
|
|
170
|
+
ask: (q) =>
|
|
171
|
+
Promise.resolve((asked.push(q), /TeX Live/.test(q) ? answer : "")),
|
|
169
172
|
hooks: false,
|
|
170
|
-
run: (
|
|
173
|
+
run: () => ({ status: 0 }),
|
|
171
174
|
link: () => ({ ok: false as const, error: "not linked in this test" }),
|
|
172
175
|
tex,
|
|
173
176
|
});
|
|
@@ -246,7 +249,7 @@ async function initRun(
|
|
|
246
249
|
err: (s: string) => err.push(s),
|
|
247
250
|
interactive: false,
|
|
248
251
|
hooks: false,
|
|
249
|
-
run: (
|
|
252
|
+
run: () => ({ status: 0 }),
|
|
250
253
|
link: () => ({ ok: false, error: "not linked in this test" }),
|
|
251
254
|
...opts,
|
|
252
255
|
});
|
|
@@ -277,6 +280,26 @@ describe("paperlint init — a paperlint.json it cannot use stops it before anyt
|
|
|
277
280
|
]);
|
|
278
281
|
});
|
|
279
282
|
|
|
283
|
+
it("🔴 a paperlint.json holding JSON that is not an object is refused, not read as settings", async () => {
|
|
284
|
+
// Guards: `null` threw a TypeError reading the papers field; `[1]` was spread into a settings
|
|
285
|
+
// object and the consumer's file rewritten as `{ "0": 1, "papersDir": … }`.
|
|
286
|
+
for (const json of ["null\n", "[1]\n"]) {
|
|
287
|
+
const root = tree({
|
|
288
|
+
"paperlint.json": json,
|
|
289
|
+
"drafts/a/paper.md": "# a\n",
|
|
290
|
+
});
|
|
291
|
+
const decl = await declarePapers(root, () =>
|
|
292
|
+
choosePapers(root, { interactive: false }),
|
|
293
|
+
);
|
|
294
|
+
expect(decl).toEqual({
|
|
295
|
+
status: "unparsable",
|
|
296
|
+
path: join(root, "paperlint.json"),
|
|
297
|
+
reason: "not a JSON object",
|
|
298
|
+
});
|
|
299
|
+
expect(readFileSync(join(root, "paperlint.json"), "utf8")).toBe(json);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
280
303
|
it("a file without a trailing newline keeps having none", async () => {
|
|
281
304
|
const root = tree({
|
|
282
305
|
"paperlint.json": '{"x":1}',
|
|
@@ -301,7 +324,7 @@ describe("paperlint init — how several candidate directories were decided, sai
|
|
|
301
324
|
it("picked by a human", async () => {
|
|
302
325
|
const r = await initRun(several(), {
|
|
303
326
|
interactive: true,
|
|
304
|
-
ask:
|
|
327
|
+
ask: () => Promise.resolve("2"),
|
|
305
328
|
});
|
|
306
329
|
expect(section(r.out)).toEqual([
|
|
307
330
|
" ✓ writing — you picked it out of 2 candidates",
|
|
@@ -319,7 +342,7 @@ describe("paperlint init — how several candidate directories were decided, sai
|
|
|
319
342
|
it("an answer that picks nothing: the first, and said so", async () => {
|
|
320
343
|
const r = await initRun(several(), {
|
|
321
344
|
interactive: true,
|
|
322
|
-
ask:
|
|
345
|
+
ask: () => Promise.resolve("nine"),
|
|
323
346
|
});
|
|
324
347
|
expect(section(r.out)[0]).toBe(
|
|
325
348
|
" ✓ drafts — 2 candidates, no answer was given, so the first one was taken",
|
|
@@ -433,7 +456,7 @@ describe("paperlint init — TeX Live and hooks failures, whole", () => {
|
|
|
433
456
|
it("TeX Live: a failed install says to run it again", async () => {
|
|
434
457
|
const t = await offerTexLive(
|
|
435
458
|
{ installed: () => false, install: () => 1 },
|
|
436
|
-
{ interactive: true, ask:
|
|
459
|
+
{ interactive: true, ask: () => Promise.resolve("yes") },
|
|
437
460
|
);
|
|
438
461
|
expect([t, reportTexLive(t, "")]).toEqual([
|
|
439
462
|
"failed",
|
|
@@ -462,8 +485,8 @@ describe("paperlint init — a first paper", () => {
|
|
|
462
485
|
const made: string[] = [];
|
|
463
486
|
const r = await initRun(tree({}), {
|
|
464
487
|
interactive: true,
|
|
465
|
-
ask:
|
|
466
|
-
createPaper:
|
|
488
|
+
ask: () => Promise.resolve(""),
|
|
489
|
+
createPaper: (_root, name) => Promise.resolve((made.push(name), 0)),
|
|
467
490
|
});
|
|
468
491
|
expect(made).toEqual([]);
|
|
469
492
|
expect(r.out[r.out.indexOf("first paper") + 1]).toBe(
|
package/src/init.ts
CHANGED
|
@@ -46,7 +46,13 @@ import {
|
|
|
46
46
|
// eslint-disable-next-line boundaries/dependencies -- legacy I/O, moves behind a port in #76
|
|
47
47
|
import { spawnSync } from "node:child_process";
|
|
48
48
|
import { dirname, join, relative, resolve } from "node:path";
|
|
49
|
-
import {
|
|
49
|
+
import {
|
|
50
|
+
doctor,
|
|
51
|
+
detectPapers,
|
|
52
|
+
found,
|
|
53
|
+
PROGRAMS,
|
|
54
|
+
type ProgramProbe,
|
|
55
|
+
} from "./doctor.ts";
|
|
50
56
|
import { PAPER_MARKERS, papersIn } from "./build.ts";
|
|
51
57
|
import { linkSkills, SKILLS_HOME, type LinkReport } from "./link-skills.ts";
|
|
52
58
|
import { actionRef } from "./action-ref.ts";
|
|
@@ -64,13 +70,16 @@ import {
|
|
|
64
70
|
nameProblem,
|
|
65
71
|
type PaperFormat,
|
|
66
72
|
} from "./new-paper.ts";
|
|
67
|
-
// The one source for the consumer's config key lives in the
|
|
68
|
-
//
|
|
73
|
+
// The one source for the consumer's config key lives in lib/ (the ESLint rules and the skill scripts
|
|
74
|
+
// import it too). It is imported compiled, from dist/, the one path that resolves the same from src/
|
|
75
|
+
// and from dist/ — see CONTRIBUTING.md.
|
|
69
76
|
import {
|
|
70
77
|
CONFIG_FILE,
|
|
71
78
|
DEFAULT_PAPERS_ROOT,
|
|
72
79
|
PAPERS_DIR_FIELD,
|
|
73
|
-
} from "
|
|
80
|
+
} from "#lib/paper-config";
|
|
81
|
+
import { messageOf } from "./domain/text.ts";
|
|
82
|
+
import { isRecord } from "./domain/record.ts";
|
|
74
83
|
|
|
75
84
|
/** How the papers directory was arrived at. Printed, because a guess must not read as a fact. */
|
|
76
85
|
export type PapersHow =
|
|
@@ -122,13 +131,21 @@ export function interactivity({
|
|
|
122
131
|
return { interactive: true, why: "a terminal on both ends" };
|
|
123
132
|
}
|
|
124
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Whether a standard stream is a terminal. Node types `isTTY` as `boolean`, but on a stream that
|
|
136
|
+
* is NOT a terminal (a pipe, a file) the property is absent — `undefined` — so it is read as the
|
|
137
|
+
* optional it really is.
|
|
138
|
+
*/
|
|
139
|
+
const isTty = (stream: { readonly isTTY?: boolean }): boolean =>
|
|
140
|
+
stream.isTTY === true;
|
|
141
|
+
|
|
125
142
|
/** The mode of THIS process — the one place `process` is read for it. */
|
|
126
143
|
export const processInteractivity = (yes: boolean) =>
|
|
127
144
|
interactivity({
|
|
128
145
|
// eslint-disable-next-line no-restricted-globals -- legacy I/O, moves behind a port in #76
|
|
129
|
-
stdinTTY:
|
|
146
|
+
stdinTTY: isTty(process.stdin),
|
|
130
147
|
// eslint-disable-next-line no-restricted-globals -- legacy I/O, moves behind a port in #76
|
|
131
|
-
stdoutTTY:
|
|
148
|
+
stdoutTTY: isTty(process.stdout),
|
|
132
149
|
// eslint-disable-next-line no-restricted-globals -- legacy I/O, moves behind a port in #76
|
|
133
150
|
env: process.env,
|
|
134
151
|
yes,
|
|
@@ -217,13 +234,17 @@ export async function declarePapers(
|
|
|
217
234
|
): Promise<DeclarationResult> {
|
|
218
235
|
const path = join(root, CONFIG_FILE);
|
|
219
236
|
const raw = existsSync(path) ? readFileSync(path, "utf8") : null;
|
|
220
|
-
let settings: Record<string, unknown
|
|
237
|
+
let settings: Readonly<Record<string, unknown>> = {};
|
|
221
238
|
if (raw !== null) {
|
|
239
|
+
let parsed: unknown;
|
|
222
240
|
try {
|
|
223
|
-
|
|
241
|
+
parsed = JSON.parse(raw);
|
|
224
242
|
} catch (e) {
|
|
225
|
-
return { status: "unparsable", path, reason: (e
|
|
243
|
+
return { status: "unparsable", path, reason: messageOf(e) };
|
|
226
244
|
}
|
|
245
|
+
if (!isRecord(parsed))
|
|
246
|
+
return { status: "unparsable", path, reason: "not a JSON object" };
|
|
247
|
+
settings = parsed;
|
|
227
248
|
const existing = settings[PAPERS_DIR_FIELD];
|
|
228
249
|
if (existing !== undefined)
|
|
229
250
|
return { status: "kept", path, papers: existing };
|
|
@@ -289,7 +310,7 @@ export async function offerWorkflow(
|
|
|
289
310
|
ask?: (q: string) => Promise<string>;
|
|
290
311
|
interactive: boolean;
|
|
291
312
|
/** The running package's version; see `InitOptions.version`. */
|
|
292
|
-
version?: string;
|
|
313
|
+
version?: string | undefined;
|
|
293
314
|
},
|
|
294
315
|
): Promise<WorkflowResult> {
|
|
295
316
|
const path = join(root, WORKFLOW_PATH);
|
|
@@ -317,7 +338,7 @@ export function reportWorkflow(
|
|
|
317
338
|
version,
|
|
318
339
|
papersDir,
|
|
319
340
|
why,
|
|
320
|
-
}: { version?: string; papersDir: string; why: string },
|
|
341
|
+
}: { version?: string | undefined; papersDir: string; why: string },
|
|
321
342
|
): string[] {
|
|
322
343
|
const ref = actionRef(version);
|
|
323
344
|
const out: string[] = [];
|
|
@@ -352,7 +373,7 @@ export function reportWorkflow(
|
|
|
352
373
|
* passing checker produce the same silence.
|
|
353
374
|
*/
|
|
354
375
|
export function missingPrograms(
|
|
355
|
-
run = spawnSync,
|
|
376
|
+
run: ProgramProbe = spawnSync,
|
|
356
377
|
): readonly (typeof PROGRAMS)[number][] {
|
|
357
378
|
return PROGRAMS.filter((p) => !found(p.bin, run));
|
|
358
379
|
}
|
|
@@ -406,7 +427,7 @@ export async function offerHooks(
|
|
|
406
427
|
hooks: boolean;
|
|
407
428
|
interactive: boolean;
|
|
408
429
|
ask?: (q: string) => Promise<string>;
|
|
409
|
-
merge?: Merge;
|
|
430
|
+
merge?: Merge | undefined;
|
|
410
431
|
},
|
|
411
432
|
): Promise<HooksOutcome> {
|
|
412
433
|
if (!hooks) return { status: "skipped" };
|
|
@@ -438,7 +459,7 @@ export async function offerHooks(
|
|
|
438
459
|
}
|
|
439
460
|
return wireHooks(root, m, shippedWiring());
|
|
440
461
|
} catch (e) {
|
|
441
|
-
return { status: "failed", reason: (e
|
|
462
|
+
return { status: "failed", reason: messageOf(e) };
|
|
442
463
|
}
|
|
443
464
|
}
|
|
444
465
|
|
|
@@ -560,7 +581,7 @@ export interface InitOptions {
|
|
|
560
581
|
/** `false` is `--no-hooks`. */
|
|
561
582
|
hooks?: boolean;
|
|
562
583
|
/** vigiles' merge. Injected only so a test can observe or replace it. */
|
|
563
|
-
merge?: Merge;
|
|
584
|
+
merge?: Merge | undefined;
|
|
564
585
|
/** `--paper <name>`: create this paper, even without a terminal. */
|
|
565
586
|
paper?: string | null;
|
|
566
587
|
/** `--format tex|md` for that paper. */
|
|
@@ -574,7 +595,7 @@ export interface InitOptions {
|
|
|
574
595
|
name: string,
|
|
575
596
|
format: PaperFormat,
|
|
576
597
|
) => Promise<number>;
|
|
577
|
-
run?:
|
|
598
|
+
run?: ProgramProbe;
|
|
578
599
|
/**
|
|
579
600
|
* What `paperlint lint` would resolve from the declaration, asked of the CLI's OWN reader. A second
|
|
580
601
|
* implementation here would be a second source of truth — the very defect `doctor` reports.
|
|
@@ -586,7 +607,7 @@ export interface InitOptions {
|
|
|
586
607
|
* The version of the running package, read by the CLI from its own `package.json`. The CI
|
|
587
608
|
* workflow is pinned to its release tag (`actionRef`); absent or unreleased, the placeholder.
|
|
588
609
|
*/
|
|
589
|
-
version?: string;
|
|
610
|
+
version?: string | undefined;
|
|
590
611
|
/**
|
|
591
612
|
* TeX Live for `paperlint build`: whether paperlint's own tree is installed, and how to install
|
|
592
613
|
* it (`paperlint toolchain`). Passed in by the CLI; without them the step only names the command.
|
|
@@ -739,21 +760,28 @@ export async function init(
|
|
|
739
760
|
for (const line of papersLines(decl, why)) log(line);
|
|
740
761
|
log(``);
|
|
741
762
|
log(`settings`);
|
|
742
|
-
|
|
763
|
+
// Every step below uses the DECLARED directory. A kept declaration outranks what init
|
|
764
|
+
// measured or guessed: otherwise the first paper and the workflow would land in the
|
|
765
|
+
// guessed directory while lint and the hooks keep reading the declared one.
|
|
766
|
+
let papersDir: string;
|
|
767
|
+
if (decl.status === "written") {
|
|
768
|
+
papersDir = decl.papers;
|
|
743
769
|
log(
|
|
744
770
|
` ✓ ${here(decl.path)} → { "${PAPERS_DIR_FIELD}": ${JSON.stringify(decl.papers)} }`,
|
|
745
771
|
);
|
|
746
|
-
else if (decl.status === "default")
|
|
772
|
+
} else if (decl.status === "default") {
|
|
773
|
+
papersDir = decl.papers;
|
|
747
774
|
log(
|
|
748
775
|
` ✓ nothing to write — "${decl.papers}" is the default, so no ${CONFIG_FILE} is needed`,
|
|
749
776
|
);
|
|
750
|
-
else if (decl.status === "kept") {
|
|
777
|
+
} else if (decl.status === "kept") {
|
|
751
778
|
if (typeof decl.papers !== "string") {
|
|
752
779
|
err(
|
|
753
780
|
` ✗ ${here(decl.path)} declares ${PAPERS_DIR_FIELD} = ${JSON.stringify(decl.papers)} — it must be a directory path (a string)`,
|
|
754
781
|
);
|
|
755
782
|
return 2;
|
|
756
783
|
}
|
|
784
|
+
papersDir = decl.papers;
|
|
757
785
|
log(
|
|
758
786
|
` ✓ ${here(decl.path)} already declares ${PAPERS_DIR_FIELD} = ${JSON.stringify(decl.papers)} — kept, nothing overwritten`,
|
|
759
787
|
);
|
|
@@ -767,10 +795,6 @@ export async function init(
|
|
|
767
795
|
);
|
|
768
796
|
return 2;
|
|
769
797
|
}
|
|
770
|
-
// Every step below uses the DECLARED directory. A kept declaration outranks what init
|
|
771
|
-
// measured or guessed: otherwise the first paper and the workflow would land in the
|
|
772
|
-
// guessed directory while lint and the hooks keep reading the declared one.
|
|
773
|
-
const papersDir = decl.papers as string;
|
|
774
798
|
|
|
775
799
|
// ── 3. the skills, linked where Claude Code looks for them ─────────────────────────────
|
|
776
800
|
for (const line of reportSkillLinks(link(root), here)) log(line);
|
|
@@ -5,11 +5,9 @@
|
|
|
5
5
|
* memory: the two error spellings `-file-line-error` produces, the missing-package form, and a
|
|
6
6
|
* marker that TeX broke at column 79.
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { createChecker } from "../lib/check.mjs";
|
|
8
|
+
import { createChecker } from "../lib/check.ts";
|
|
9
|
+
import type { LogMarker } from "./latex-log.ts";
|
|
11
10
|
|
|
12
|
-
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
13
11
|
const {
|
|
14
12
|
unwrapLog,
|
|
15
13
|
logMarkers,
|
|
@@ -18,7 +16,7 @@ const {
|
|
|
18
16
|
bibtexExcerpt,
|
|
19
17
|
auxBib,
|
|
20
18
|
MAX_PRINT_LINE,
|
|
21
|
-
} = await import(
|
|
19
|
+
} = await import("./latex-log.ts");
|
|
22
20
|
|
|
23
21
|
const check = createChecker();
|
|
24
22
|
|
|
@@ -78,13 +76,15 @@ const firstPass = [
|
|
|
78
76
|
const m = logMarkers(firstPass);
|
|
79
77
|
check(
|
|
80
78
|
"every documented marker is recognised",
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
79
|
+
(
|
|
80
|
+
[
|
|
81
|
+
"rerun-requested",
|
|
82
|
+
"labels-changed",
|
|
83
|
+
"rerunfilecheck",
|
|
84
|
+
"undefined-references",
|
|
85
|
+
"undefined-citations",
|
|
86
|
+
] satisfies LogMarker[]
|
|
87
|
+
).every((k) => m.includes(k)),
|
|
88
88
|
);
|
|
89
89
|
check(
|
|
90
90
|
"a clean log carries no markers",
|
|
@@ -96,7 +96,7 @@ check(
|
|
|
96
96
|
// Guards: matching the documented summary line rather than the word 'undefined'.
|
|
97
97
|
check(
|
|
98
98
|
"a single reference warning is not the end-of-run summary",
|
|
99
|
-
!logMarkers(
|
|
99
|
+
!logMarkers(firstPass.slice(0, 1)).includes("undefined-references"),
|
|
100
100
|
);
|
|
101
101
|
|
|
102
102
|
// ── error excerpt ───────────────────────────────────────────────────────────────────────
|
|
@@ -114,7 +114,7 @@ check(
|
|
|
114
114
|
"-file-line-error form: the error line, the l.NNN line and the rest of the source line",
|
|
115
115
|
ex[0] === "./paper.tex:4: Undefined control sequence." &&
|
|
116
116
|
ex[1] === "l.4 \\foo" &&
|
|
117
|
-
ex[2].includes("bar baz"),
|
|
117
|
+
(ex[2] ?? "").includes("bar baz"),
|
|
118
118
|
);
|
|
119
119
|
// Guards: the end of the excerpt — without it the excerpt runs into TeX's memory statistics and
|
|
120
120
|
// buries the line the author needs.
|
|
@@ -6,30 +6,43 @@
|
|
|
6
6
|
* A defect in `nextStep` fails in table 1, one in `summarize` in table 2, before table 3 runs.
|
|
7
7
|
*
|
|
8
8
|
* The decision is pure, so none of this needs TeX. The real-pdflatex half lives in
|
|
9
|
-
* `test/e2e/build.
|
|
9
|
+
* `test/e2e/build.ts`.
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
11
|
+
import { createChecker } from "../lib/check.ts";
|
|
12
|
+
import type {
|
|
13
|
+
BibInput,
|
|
14
|
+
Hashes,
|
|
15
|
+
Observation,
|
|
16
|
+
State,
|
|
17
|
+
Step,
|
|
18
|
+
} from "./latex-loop.ts";
|
|
14
19
|
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
join(HERE, "latex-loop.ts")
|
|
18
|
-
);
|
|
20
|
+
const { nextStep, summarize, changedFiles, MAX_PASSES } =
|
|
21
|
+
await import("./latex-loop.ts");
|
|
19
22
|
|
|
20
23
|
const check = createChecker();
|
|
21
24
|
|
|
22
25
|
// ── builders ────────────────────────────────────────────────────────────────────────────
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
+
type LatexPass = Extract<Observation, { step: "latex" }>;
|
|
27
|
+
type BibtexPass = Extract<Observation, { step: "bibtex" }>;
|
|
28
|
+
const H = (
|
|
29
|
+
aux: string | null,
|
|
30
|
+
toc: string | null = null,
|
|
31
|
+
out: string | null = null,
|
|
32
|
+
bbl: string | null = null,
|
|
33
|
+
): Hashes => ({ aux, toc, out, bbl });
|
|
34
|
+
const NO_BIB: BibInput = { kind: "none" };
|
|
35
|
+
const bib = (
|
|
36
|
+
citations: readonly string[],
|
|
37
|
+
bibHash: string | null = "b1",
|
|
38
|
+
): BibInput => ({
|
|
26
39
|
kind: "needed",
|
|
27
40
|
citations,
|
|
28
41
|
databases: ["refs"],
|
|
29
42
|
style: "plain",
|
|
30
43
|
bibHash,
|
|
31
44
|
});
|
|
32
|
-
const latex = (o = {}) => ({
|
|
45
|
+
const latex = (o: Partial<LatexPass> = {}): LatexPass => ({
|
|
33
46
|
step: "latex",
|
|
34
47
|
final: false,
|
|
35
48
|
exitCode: 0,
|
|
@@ -40,7 +53,7 @@ const latex = (o = {}) => ({
|
|
|
40
53
|
errorLines: [],
|
|
41
54
|
...o,
|
|
42
55
|
});
|
|
43
|
-
const bibtex = (o = {}) => ({
|
|
56
|
+
const bibtex = (o: Partial<BibtexPass> = {}): BibtexPass => ({
|
|
44
57
|
step: "bibtex",
|
|
45
58
|
exitCode: 0,
|
|
46
59
|
before: H("a1"),
|
|
@@ -49,11 +62,11 @@ const bibtex = (o = {}) => ({
|
|
|
49
62
|
errorLines: [],
|
|
50
63
|
...o,
|
|
51
64
|
});
|
|
52
|
-
const kind = (s) =>
|
|
65
|
+
const kind = (s: Step) =>
|
|
53
66
|
s.kind === "latex" ? `latex${s.final ? "-final" : ""}` : s.kind;
|
|
54
67
|
|
|
55
68
|
// ── 1. nextStep over a State ─────────────────────────────────────────────────────────────
|
|
56
|
-
const S = (o = {}) => ({
|
|
69
|
+
const S = (o: Partial<State> = {}): State => ({
|
|
57
70
|
failed: null,
|
|
58
71
|
bibOutdated: false,
|
|
59
72
|
unsettled: [],
|
|
@@ -62,12 +75,12 @@ const S = (o = {}) => ({
|
|
|
62
75
|
warnings: [],
|
|
63
76
|
...o,
|
|
64
77
|
});
|
|
65
|
-
const exitFail = {
|
|
78
|
+
const exitFail: NonNullable<State["failed"]> = {
|
|
66
79
|
step: "bibtex",
|
|
67
80
|
cause: { kind: "exit", code: 2 },
|
|
68
81
|
lines: ["I couldn't open database file nope.bib"],
|
|
69
82
|
};
|
|
70
|
-
const STATES = [
|
|
83
|
+
const STATES: [string, State, string][] = [
|
|
71
84
|
// Guards: the only moment paper-guards can speak — its error comes on the final pass, so testing
|
|
72
85
|
// 'final' before 'exit code' would ship the undefined reference green.
|
|
73
86
|
[
|
|
@@ -96,7 +109,7 @@ const STATES = [
|
|
|
96
109
|
// Guards: the stop on a document that never settles — without it the loop runs forever; with a
|
|
97
110
|
// silent cap it ships a PDF with stale cross-references.
|
|
98
111
|
[
|
|
99
|
-
`Q3 cap: ${MAX_PASSES} passes and still unsettled — fail`,
|
|
112
|
+
`Q3 cap: ${String(MAX_PASSES)} passes and still unsettled — fail`,
|
|
100
113
|
S({ unsettled: ["paper.aux"], latexPasses: MAX_PASSES }),
|
|
101
114
|
"fail",
|
|
102
115
|
],
|
|
@@ -115,20 +128,29 @@ for (const [label, state, expected] of STATES) {
|
|
|
115
128
|
const q1 = nextStep(S({ failed: exitFail }));
|
|
116
129
|
check(
|
|
117
130
|
"Q1 passes the failure through: the step, the cause and the lines verbatim",
|
|
118
|
-
q1.
|
|
131
|
+
q1.kind === "fail" &&
|
|
132
|
+
q1.step === "bibtex" &&
|
|
133
|
+
q1.cause.kind === "exit" &&
|
|
134
|
+
q1.cause.code === 2 &&
|
|
135
|
+
q1.lines === exitFail.lines,
|
|
119
136
|
);
|
|
120
137
|
const q3cap = nextStep(
|
|
121
138
|
S({ unsettled: ["paper.aux", "labels-changed"], latexPasses: MAX_PASSES }),
|
|
122
139
|
);
|
|
123
140
|
check(
|
|
124
141
|
"the cap's failure is no-convergence and NAMES what stayed unsettled",
|
|
125
|
-
q3cap.
|
|
126
|
-
q3cap.
|
|
142
|
+
q3cap.kind === "fail" &&
|
|
143
|
+
q3cap.cause.kind === "no-convergence" &&
|
|
144
|
+
(q3cap.lines[0] ?? "").includes("paper.aux, labels-changed"),
|
|
127
145
|
);
|
|
128
146
|
check(
|
|
129
147
|
"done carries the state's warnings",
|
|
130
|
-
|
|
131
|
-
|
|
148
|
+
(() => {
|
|
149
|
+
const done = nextStep(
|
|
150
|
+
S({ finalDone: true, warnings: ["undefined-citations"] }),
|
|
151
|
+
);
|
|
152
|
+
return done.kind === "done" && done.warnings[0] === "undefined-citations";
|
|
153
|
+
})(),
|
|
132
154
|
);
|
|
133
155
|
|
|
134
156
|
// ── 2. summarize over histories ─────────────────────────────────────────────────────────
|
|
@@ -152,7 +174,10 @@ check(
|
|
|
152
174
|
(() => {
|
|
153
175
|
const f = sum([latex({ exitCode: 1, errorLines: ["l.4 \\foo"] })]).failed;
|
|
154
176
|
return (
|
|
155
|
-
f?.step === "latex" &&
|
|
177
|
+
f?.step === "latex" &&
|
|
178
|
+
f.cause.kind === "exit" &&
|
|
179
|
+
f.cause.code === 1 &&
|
|
180
|
+
f.lines[0] === "l.4 \\foo"
|
|
156
181
|
);
|
|
157
182
|
})(),
|
|
158
183
|
);
|
|
@@ -162,22 +187,22 @@ check(
|
|
|
162
187
|
);
|
|
163
188
|
check(
|
|
164
189
|
"summarize: bibOutdated when the aux names a bibliography bibtex has not run on",
|
|
165
|
-
sum([latex({ bib: bib(["k"]) })]).bibOutdated
|
|
190
|
+
sum([latex({ bib: bib(["k"]) })]).bibOutdated,
|
|
166
191
|
);
|
|
167
192
|
check(
|
|
168
193
|
"summarize: bibOutdated is false once bibtex ran on exactly that input",
|
|
169
|
-
sum([latex({ bib: bib(["k"]) }), bibtex()]).bibOutdated
|
|
194
|
+
!sum([latex({ bib: bib(["k"]) }), bibtex()]).bibOutdated,
|
|
170
195
|
);
|
|
171
196
|
// Guards: the .bib hash as an input — a corrected author or year in refs.bib would keep the old
|
|
172
197
|
// .bbl, and the PDF would print the entry as it was before the fix.
|
|
173
198
|
check(
|
|
174
199
|
"summarize: the .bib content changed since bibtex ran — bibOutdated",
|
|
175
200
|
sum([latex({ bib: bib(["k"]) }), bibtex(), latex({ bib: bib(["k"], "b2") })])
|
|
176
|
-
.bibOutdated
|
|
201
|
+
.bibOutdated,
|
|
177
202
|
);
|
|
178
203
|
check(
|
|
179
204
|
"summarize: no bibliography at all — never bibOutdated",
|
|
180
|
-
sum([latex()]).bibOutdated
|
|
205
|
+
!sum([latex()]).bibOutdated,
|
|
181
206
|
);
|
|
182
207
|
check(
|
|
183
208
|
"summarize: unsettled names every tracked file the last pass changed",
|
|
@@ -232,10 +257,10 @@ check(
|
|
|
232
257
|
);
|
|
233
258
|
|
|
234
259
|
// ── 3. composed: nextStep(summarize(history)) ───────────────────────────────────────────
|
|
235
|
-
const step = (history) => nextStep(summarize(history));
|
|
260
|
+
const step = (history: readonly Observation[]) => nextStep(summarize(history));
|
|
236
261
|
|
|
237
262
|
// ── the histories the loop sees ───────────────────────────────────────────────────────────────────────────
|
|
238
|
-
const TABLE = [
|
|
263
|
+
const TABLE: [string, Observation[], string][] = [
|
|
239
264
|
["nothing ran yet — the first step is a plain pass", [], "latex"],
|
|
240
265
|
[
|
|
241
266
|
"a pass that created the aux — rerun",
|
|
@@ -364,7 +389,8 @@ const latexFail = step([
|
|
|
364
389
|
]);
|
|
365
390
|
check(
|
|
366
391
|
"a latex fail names the step, the exit code and the log excerpt verbatim",
|
|
367
|
-
latexFail.
|
|
392
|
+
latexFail.kind === "fail" &&
|
|
393
|
+
latexFail.step === "latex" &&
|
|
368
394
|
latexFail.cause.kind === "exit" &&
|
|
369
395
|
latexFail.cause.code === 1 &&
|
|
370
396
|
latexFail.lines[1] === "l.4 \\foo",
|
|
@@ -380,14 +406,16 @@ check(
|
|
|
380
406
|
"a bibtex fail names BIBTEX, not latex",
|
|
381
407
|
bibFail.kind === "fail" &&
|
|
382
408
|
bibFail.step === "bibtex" &&
|
|
409
|
+
bibFail.cause.kind === "exit" &&
|
|
383
410
|
bibFail.cause.code === 2,
|
|
384
411
|
);
|
|
385
412
|
|
|
386
413
|
// ── the cap ─────────────────────────────────────────────────────────────────────────────
|
|
387
|
-
const moving = (i
|
|
414
|
+
const moving = (i: number) =>
|
|
415
|
+
latex({ before: H(`a${String(i)}`), after: H(`a${String(i + 1)}`) });
|
|
388
416
|
const underCap = Array.from({ length: MAX_PASSES - 1 }, (_, i) => moving(i));
|
|
389
417
|
check(
|
|
390
|
-
`the cap is ${MAX_PASSES} and it is not hit early: after ${MAX_PASSES - 1} moving passes, one more`,
|
|
418
|
+
`the cap is ${String(MAX_PASSES)} and it is not hit early: after ${String(MAX_PASSES - 1)} moving passes, one more`,
|
|
391
419
|
MAX_PASSES === 5 && kind(step(underCap)) === "latex",
|
|
392
420
|
);
|
|
393
421
|
const atCap = Array.from({ length: MAX_PASSES }, (_, i) => moving(i));
|
|
@@ -398,7 +426,7 @@ check(
|
|
|
398
426
|
);
|
|
399
427
|
check(
|
|
400
428
|
"the non-convergence failure NAMES the file that kept changing",
|
|
401
|
-
capped.kind === "fail" && capped.lines[0].includes("paper.aux"),
|
|
429
|
+
capped.kind === "fail" && (capped.lines[0] ?? "").includes("paper.aux"),
|
|
402
430
|
);
|
|
403
431
|
const cappedMarker = step(
|
|
404
432
|
Array.from({ length: MAX_PASSES }, () =>
|
|
@@ -408,7 +436,7 @@ const cappedMarker = step(
|
|
|
408
436
|
check(
|
|
409
437
|
"a log that keeps asking for a rerun hits the same cap, and names the marker",
|
|
410
438
|
cappedMarker.kind === "fail" &&
|
|
411
|
-
cappedMarker.lines[0].includes("labels-changed"),
|
|
439
|
+
(cappedMarker.lines[0] ?? "").includes("labels-changed"),
|
|
412
440
|
);
|
|
413
441
|
const convergedAtCap = [...atCap.slice(0, -1), latex()];
|
|
414
442
|
check(
|
|
@@ -446,5 +474,5 @@ check(
|
|
|
446
474
|
);
|
|
447
475
|
|
|
448
476
|
console.log(
|
|
449
|
-
`✓ ${String(check.count)} assertions passed — latex-loop: nextStep over State, summarize over histories, and the two composed, including the ${MAX_PASSES}-pass cap`,
|
|
477
|
+
`✓ ${String(check.count)} assertions passed — latex-loop: nextStep over State, summarize over histories, and the two composed, including the ${String(MAX_PASSES)}-pass cap`,
|
|
450
478
|
);
|
package/src/latex-loop.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* PURE. No disk, no process, no clock. The shell in `build.ts` runs a step, records what it saw
|
|
5
5
|
* as an `Observation`, appends it to the history and asks `nextStep(summarize(history))` again.
|
|
6
|
-
* Both halves are table tests (`latex-loop.harness.
|
|
6
|
+
* Both halves are table tests (`latex-loop.harness.ts`), not scenarios that need TeX.
|
|
7
7
|
*
|
|
8
8
|
* ── THE LOOP: `summarize` reduces the history to a `State`, and `nextStep` asks four questions ──
|
|
9
9
|
* 1. did the last program fail? → fail, with its error lines
|
|
@@ -118,7 +118,7 @@ export type Step =
|
|
|
118
118
|
};
|
|
119
119
|
|
|
120
120
|
/** Non-final pdflatex passes allowed before the build is declared non-converging. */
|
|
121
|
-
export const MAX_PASSES = 5;
|
|
121
|
+
export const MAX_PASSES: number = 5;
|
|
122
122
|
|
|
123
123
|
/** The history, reduced to the four questions `nextStep` asks. */
|
|
124
124
|
export type State = {
|
|
@@ -152,7 +152,7 @@ const noConvergence = (unsettled: readonly string[]): Step => ({
|
|
|
152
152
|
step: "latex",
|
|
153
153
|
cause: { kind: "no-convergence", unsettled },
|
|
154
154
|
lines: [
|
|
155
|
-
`the build does not converge: after ${MAX_PASSES} pdflatex passes, the last one still changed or asked to rerun: ${unsettled.join(", ")}.`,
|
|
155
|
+
`the build does not converge: after ${String(MAX_PASSES)} pdflatex passes, the last one still changed or asked to rerun: ${unsettled.join(", ")}.`,
|
|
156
156
|
`A document whose aux keeps changing on every pass never settles; stopping here instead of`,
|
|
157
157
|
`shipping a PDF with stale cross-references.`,
|
|
158
158
|
],
|