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
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* The fixture package keeps its skills where the real one does, under `SHIPPED_SKILLS_DIR` from
|
|
13
13
|
* consumer.mjs — the same constant the linker and the install e2e read.
|
|
14
14
|
*
|
|
15
|
-
* Run: node src/link-skills.harness.
|
|
15
|
+
* Run: node src/link-skills.harness.ts
|
|
16
16
|
*/
|
|
17
17
|
import {
|
|
18
18
|
existsSync,
|
|
@@ -26,17 +26,14 @@ import {
|
|
|
26
26
|
writeFileSync,
|
|
27
27
|
} from "node:fs";
|
|
28
28
|
import { tmpdir } from "node:os";
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
29
|
+
import { isAbsolute, join } from "node:path";
|
|
30
|
+
import { createChecker } from "../lib/check.ts";
|
|
31
|
+
import type { LinkReport } from "./link-skills.ts";
|
|
32
32
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
);
|
|
37
|
-
const { SHIPPED_SKILLS_DIR: SHIPS, PACKAGE_NAME: PKG } = await import(
|
|
38
|
-
join(HERE, "..", "skills", "paper-pipeline", "scripts", "consumer.mjs")
|
|
39
|
-
);
|
|
33
|
+
const { linkSkills, locatePackage, shippedSkills } =
|
|
34
|
+
await import("./link-skills.ts");
|
|
35
|
+
const { SHIPPED_SKILLS_DIR: SHIPS, PACKAGE_NAME: PKG } =
|
|
36
|
+
await import("../skills/paper-pipeline/scripts/consumer.mjs");
|
|
40
37
|
|
|
41
38
|
const check = createChecker();
|
|
42
39
|
|
|
@@ -46,7 +43,7 @@ const work = realpathSync(
|
|
|
46
43
|
);
|
|
47
44
|
|
|
48
45
|
/** A package as a manager would unpack it: manifest, skills, one non-skill. */
|
|
49
|
-
function writePackage(dir) {
|
|
46
|
+
function writePackage(dir: string): void {
|
|
50
47
|
mkdirSync(dir, { recursive: true });
|
|
51
48
|
writeFileSync(
|
|
52
49
|
join(dir, "package.json"),
|
|
@@ -63,7 +60,7 @@ function writePackage(dir) {
|
|
|
63
60
|
}
|
|
64
61
|
|
|
65
62
|
/** A consumer project with the package installed the npm way or the pnpm way. */
|
|
66
|
-
function consumer(name, manager = "npm") {
|
|
63
|
+
function consumer(name: string, manager = "npm"): string {
|
|
67
64
|
const dir = join(work, name);
|
|
68
65
|
mkdirSync(join(dir, "node_modules"), { recursive: true });
|
|
69
66
|
writeFileSync(
|
|
@@ -89,8 +86,11 @@ function consumer(name, manager = "npm") {
|
|
|
89
86
|
return dir;
|
|
90
87
|
}
|
|
91
88
|
|
|
92
|
-
const home = (dir) => join(dir, ".claude", "skills");
|
|
93
|
-
|
|
89
|
+
const home = (dir: string) => join(dir, ".claude", "skills");
|
|
90
|
+
/** The report's links; a refusal has none. */
|
|
91
|
+
const linksOf = (report: LinkReport) => (report.ok ? report.links : []);
|
|
92
|
+
const status = (report: LinkReport, name: string) =>
|
|
93
|
+
linksOf(report).find((l) => l.name === name);
|
|
94
94
|
|
|
95
95
|
try {
|
|
96
96
|
// ── I. A FRESH npm CONSUMER: EVERY DECLARED SKILL IS LINKED, AND ONLY SKILLS ───────────
|
|
@@ -99,11 +99,11 @@ try {
|
|
|
99
99
|
const r = linkSkills(dir);
|
|
100
100
|
check(
|
|
101
101
|
"🔴 it links every skill the package ships",
|
|
102
|
-
r.ok
|
|
102
|
+
r.ok && SKILLS.every((s) => status(r, s)?.status === "created"),
|
|
103
103
|
);
|
|
104
104
|
check(
|
|
105
105
|
"and nothing that is not a skill — a directory without SKILL.md is not one",
|
|
106
|
-
r.
|
|
106
|
+
linksOf(r).length === SKILLS.length &&
|
|
107
107
|
!existsSync(join(home(dir), "shared")),
|
|
108
108
|
);
|
|
109
109
|
check(
|
|
@@ -195,7 +195,7 @@ try {
|
|
|
195
195
|
const r = linkSkills(dir);
|
|
196
196
|
check(
|
|
197
197
|
"a project that does not have the package gets an error, not links into somebody's cache",
|
|
198
|
-
r.ok
|
|
198
|
+
!r.ok && /does not resolve from/.test(r.error),
|
|
199
199
|
);
|
|
200
200
|
check("and nothing is created", !existsSync(join(dir, ".claude")));
|
|
201
201
|
}
|
|
@@ -229,5 +229,5 @@ try {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
console.log(
|
|
232
|
-
`✓ ${check.count} assertions passed — link-skills: the skills are where Claude Code looks`,
|
|
232
|
+
`✓ ${String(check.count)} assertions passed — link-skills: the skills are where Claude Code looks`,
|
|
233
233
|
);
|
package/src/link-skills.test.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from "node:fs";
|
|
15
15
|
import { join } from "node:path";
|
|
16
16
|
import { test } from "vitest";
|
|
17
|
-
import { useTempDir, writeTree } from "../test/support.
|
|
17
|
+
import { useTempDir, writeTree } from "../test/support.ts";
|
|
18
18
|
import { linkSkills, locatePackage, type LinkFs } from "./link-skills.ts";
|
|
19
19
|
|
|
20
20
|
const root = useTempDir("paperlint-link-skills-");
|
|
@@ -62,7 +62,7 @@ test("a package without a skills directory, or one holding a dangling link, is n
|
|
|
62
62
|
symlinkSync(join(root, "gone"), join(dangling, "skills", "b"));
|
|
63
63
|
const r = linkSkills(join(root, "p2"), { locate: at(dangling) });
|
|
64
64
|
assert.equal(r.ok, false);
|
|
65
|
-
assert.match(
|
|
65
|
+
assert.match(r.error, /^1 skill link\(s\) in .* lead nowhere:/);
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
test("a skills home that cannot be made is named", () => {
|
|
@@ -71,7 +71,7 @@ test("a skills home that cannot be made is named", () => {
|
|
|
71
71
|
});
|
|
72
72
|
const r = linkSkills(project, { locate: at(pkg("home", ["a"])) });
|
|
73
73
|
assert.equal(r.ok, false);
|
|
74
|
-
assert.match(
|
|
74
|
+
assert.match(r.error, /^cannot create .*\.claude\/skills: /);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
test("entries that are someone else's: a plain file, one that cannot be resolved, a link that cannot be made", () => {
|
package/src/link-skills.ts
CHANGED
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
PACKAGE_NAME,
|
|
45
45
|
SHIPPED_SKILLS_DIR,
|
|
46
46
|
} from "../skills/paper-pipeline/scripts/consumer.mjs";
|
|
47
|
+
import { messageOf, codeOf } from "./domain/text.ts";
|
|
47
48
|
|
|
48
49
|
export { PACKAGE_NAME };
|
|
49
50
|
/** Where Claude Code looks for project skills, relative to the project root. */
|
|
@@ -74,7 +75,7 @@ export function locatePackage(
|
|
|
74
75
|
manifest = resolveManifest(project);
|
|
75
76
|
} catch (e) {
|
|
76
77
|
return {
|
|
77
|
-
error: `${(e
|
|
78
|
+
error: `${codeOf(e) ?? "error"}: ${String(messageOf(e).split("\n")[0])}`,
|
|
78
79
|
};
|
|
79
80
|
}
|
|
80
81
|
const dir = realpathSync(dirname(manifest));
|
|
@@ -108,7 +109,7 @@ export function shippedSkills(
|
|
|
108
109
|
try {
|
|
109
110
|
return { skillsDir, names: installedSkills(skillsDir) };
|
|
110
111
|
} catch (e) {
|
|
111
|
-
return { error: (e
|
|
112
|
+
return { error: messageOf(e) };
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
|
|
@@ -207,7 +208,7 @@ function linkOne(
|
|
|
207
208
|
return {
|
|
208
209
|
name,
|
|
209
210
|
status: "foreign",
|
|
210
|
-
reason: `could not create the link: ${(e
|
|
211
|
+
reason: `could not create the link: ${messageOf(e)}`,
|
|
211
212
|
};
|
|
212
213
|
}
|
|
213
214
|
}
|
|
@@ -218,7 +219,7 @@ function makeHome(home: string): string | null {
|
|
|
218
219
|
mkdirSync(home, { recursive: true });
|
|
219
220
|
return null;
|
|
220
221
|
} catch (e) {
|
|
221
|
-
return `cannot create ${home}: ${(e
|
|
222
|
+
return `cannot create ${home}: ${messageOf(e)}`;
|
|
222
223
|
}
|
|
223
224
|
}
|
|
224
225
|
|
package/src/lint-fix.test.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { tmpdir } from "node:os";
|
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
import { afterEach, describe, expect, it } from "vitest";
|
|
15
15
|
import { run } from "./cli.ts";
|
|
16
|
+
import { lintReport } from "../test/lint-report.ts";
|
|
16
17
|
|
|
17
18
|
const dirs: string[] = [];
|
|
18
19
|
afterEach(() => {
|
|
@@ -50,8 +51,8 @@ async function lint(root: string, args: string[] = []) {
|
|
|
50
51
|
return { code, out: out.join("\n"), err: err.join("\n") };
|
|
51
52
|
}
|
|
52
53
|
const ruleIds = (stdout: string): string[] =>
|
|
53
|
-
(
|
|
54
|
-
r.messages.map((m) => m.ruleId),
|
|
54
|
+
lintReport(stdout).flatMap((r) =>
|
|
55
|
+
r.messages.map((m) => m.ruleId ?? "(fatal)"),
|
|
55
56
|
);
|
|
56
57
|
|
|
57
58
|
describe("paperlint lint --fix", () => {
|
|
@@ -85,10 +86,7 @@ describe("sibling cards: `read:` is required, from the package's own schema", ()
|
|
|
85
86
|
"papers/a/siblings/jones2024.md": "---\nread: abstract\n---\n# Jones\n",
|
|
86
87
|
},
|
|
87
88
|
);
|
|
88
|
-
const results =
|
|
89
|
-
filePath: string;
|
|
90
|
-
messages: { ruleId: string; severity: number }[];
|
|
91
|
-
}[];
|
|
89
|
+
const results = lintReport((await lint(root)).out);
|
|
92
90
|
const of = (name: string) =>
|
|
93
91
|
results.find((r) => r.filePath.endsWith(name))?.messages ?? null;
|
|
94
92
|
expect(of("smith2025.md")?.map((m) => [m.ruleId, m.severity])).toEqual([
|
package/src/linted-set.test.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { tmpdir } from "node:os";
|
|
|
19
19
|
import { join, relative } from "node:path";
|
|
20
20
|
import { afterEach, describe, expect, it } from "vitest";
|
|
21
21
|
import { run } from "./cli.ts";
|
|
22
|
+
import { lintReport } from "../test/lint-report.ts";
|
|
22
23
|
|
|
23
24
|
const dirs: string[] = [];
|
|
24
25
|
afterEach(() => {
|
|
@@ -91,9 +92,8 @@ async function lintedSet(root: string, args: string[]): Promise<string[]> {
|
|
|
91
92
|
log: (s: string) => out.push(s),
|
|
92
93
|
err: () => {},
|
|
93
94
|
});
|
|
94
|
-
type Result = { filePath: string; messages: { ruleId: string | null }[] };
|
|
95
95
|
return (
|
|
96
|
-
(
|
|
96
|
+
lintReport(out.join("\n"))
|
|
97
97
|
// A structure finding reports a MISSING file in the same shape; it was not linted.
|
|
98
98
|
.filter(
|
|
99
99
|
(r) => !r.messages.some((m) => m.ruleId === "structure/required-file"),
|
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
* from a missing file into a created one;
|
|
8
8
|
* - the scorecard carries `researchQuestion` and NO `stages` — a new paper has shipped nothing,
|
|
9
9
|
* and a placeholder stage would be a red `paper/stages` on the very first run;
|
|
10
|
-
* - the scorecard
|
|
11
|
-
*
|
|
12
|
-
* skills' checker reads;
|
|
10
|
+
* - the scorecard carries the `**Readiness verdict:**` line the status hook surfaces on every
|
|
11
|
+
* paper edit — without it the hook reports that the paper cannot say whether it is ready;
|
|
13
12
|
* - it never overwrites, and on an existing folder adds only what is missing;
|
|
14
13
|
* - the project's `<papers>/.template/` wins over the package, file by file.
|
|
15
14
|
*
|
|
@@ -25,13 +24,12 @@ import {
|
|
|
25
24
|
rmSync,
|
|
26
25
|
writeFileSync,
|
|
27
26
|
} from "node:fs";
|
|
28
|
-
import { spawnSync } from "node:child_process";
|
|
29
27
|
import { tmpdir } from "node:os";
|
|
30
28
|
import { dirname, join } from "node:path";
|
|
31
29
|
import { fileURLToPath } from "node:url";
|
|
32
30
|
import { load } from "js-yaml";
|
|
33
|
-
import { frontmatterBlock } from "../lib/markdown.
|
|
34
|
-
import { createChecker } from "../lib/check.
|
|
31
|
+
import { frontmatterBlock } from "../lib/markdown.ts";
|
|
32
|
+
import { createChecker } from "../lib/check.ts";
|
|
35
33
|
|
|
36
34
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
37
35
|
const ROOT = dirname(HERE);
|
|
@@ -124,26 +122,12 @@ try {
|
|
|
124
122
|
/1 problem \(0 errors, 1 warning\)/.test(l.text),
|
|
125
123
|
);
|
|
126
124
|
|
|
127
|
-
//
|
|
128
|
-
const pc = spawnSync(
|
|
129
|
-
process.execPath,
|
|
130
|
-
[
|
|
131
|
-
join(ROOT, "skills", "paper-pipeline", "scripts", "pipeline-check.mjs"),
|
|
132
|
-
join(papers, "demo"),
|
|
133
|
-
"--json",
|
|
134
|
-
"--today=2026-09-23",
|
|
135
|
-
],
|
|
136
|
-
{ cwd: work, encoding: "utf8" },
|
|
137
|
-
);
|
|
138
|
-
let parsed = null;
|
|
139
|
-
try {
|
|
140
|
-
parsed = JSON.parse(pc.stdout);
|
|
141
|
-
} catch {
|
|
142
|
-
parsed = null;
|
|
143
|
-
}
|
|
125
|
+
// What the status hook reads on every paper edit: the verdict line.
|
|
144
126
|
check(
|
|
145
|
-
"the scorecard
|
|
146
|
-
|
|
127
|
+
"the scorecard carries the `**Readiness verdict:**` line the status hook surfaces",
|
|
128
|
+
/^\*\*Readiness verdict:\*\*/m.test(
|
|
129
|
+
readFileSync(join(papers, "demo", STATUS_FILE), "utf8"),
|
|
130
|
+
),
|
|
147
131
|
);
|
|
148
132
|
|
|
149
133
|
// ── never overwrite; on an existing folder only the missing files ─────────────────────
|
package/src/new-paper.test.ts
CHANGED
|
@@ -20,6 +20,8 @@ import { parsePaperSettings } from "./paper-settings.ts";
|
|
|
20
20
|
import { chooseVenue, run } from "./cli.ts";
|
|
21
21
|
import { shippedPresets } from "./presets.ts";
|
|
22
22
|
import { packageVenuesDir } from "../skills/paper-pipeline/scripts/consumer.mjs";
|
|
23
|
+
import { lintReport } from "../test/lint-report.ts";
|
|
24
|
+
import { z } from "zod";
|
|
23
25
|
|
|
24
26
|
const dirs: string[] = [];
|
|
25
27
|
afterEach(() => {
|
|
@@ -31,10 +33,9 @@ const tmp = () => {
|
|
|
31
33
|
return d;
|
|
32
34
|
};
|
|
33
35
|
const settingsOf = (dir: string) =>
|
|
34
|
-
|
|
35
|
-
string,
|
|
36
|
-
|
|
37
|
-
>;
|
|
36
|
+
z
|
|
37
|
+
.record(z.string(), z.unknown())
|
|
38
|
+
.parse(JSON.parse(readFileSync(join(dir, "paperlint.json"), "utf8")));
|
|
38
39
|
|
|
39
40
|
describe("paperlint new — paperlint.json", () => {
|
|
40
41
|
it("is always written, from the package template: no venue chosen yet, and valid", () => {
|
|
@@ -107,11 +108,7 @@ describe("paperlint lint — a paper with no venue preset chosen", () => {
|
|
|
107
108
|
log: (s: string) => out.push(s),
|
|
108
109
|
err: () => {},
|
|
109
110
|
});
|
|
110
|
-
const messages = (
|
|
111
|
-
JSON.parse(out.join("\n")) as {
|
|
112
|
-
messages: { ruleId: string; severity: number; message: string }[];
|
|
113
|
-
}[]
|
|
114
|
-
).flatMap((r) => r.messages);
|
|
111
|
+
const messages = lintReport(out.join("\n")).flatMap((r) => r.messages);
|
|
115
112
|
return { code, messages };
|
|
116
113
|
}
|
|
117
114
|
|
|
@@ -266,7 +263,8 @@ describe("chooseVenue — on a terminal", () => {
|
|
|
266
263
|
paperDir: "/p/papers/demo",
|
|
267
264
|
cwd: "/p",
|
|
268
265
|
interactive: true,
|
|
269
|
-
ask:
|
|
266
|
+
ask: (q: string) =>
|
|
267
|
+
Promise.resolve((asked.push(q), answers.shift() ?? "")),
|
|
270
268
|
},
|
|
271
269
|
};
|
|
272
270
|
};
|
package/src/new-paper.ts
CHANGED
|
@@ -25,7 +25,8 @@ import {
|
|
|
25
25
|
/* eslint-enable boundaries/dependencies */
|
|
26
26
|
import { join } from "node:path";
|
|
27
27
|
import { fileURLToPath } from "node:url";
|
|
28
|
-
import { CONFIG_FILE } from "
|
|
28
|
+
import { CONFIG_FILE } from "#lib/paper-config";
|
|
29
|
+
import { isRecord } from "./domain/record.ts";
|
|
29
30
|
|
|
30
31
|
export type PaperFormat = "tex" | "md";
|
|
31
32
|
export const FORMATS: readonly PaperFormat[] = ["tex", "md"];
|
|
@@ -50,7 +51,7 @@ export const SOURCE_FILE: Readonly<Record<PaperFormat, string>> = {
|
|
|
50
51
|
};
|
|
51
52
|
|
|
52
53
|
export const isFormat = (v: unknown): v is PaperFormat =>
|
|
53
|
-
FORMATS.
|
|
54
|
+
FORMATS.some((f) => f === v);
|
|
54
55
|
|
|
55
56
|
/**
|
|
56
57
|
* Why a name is refused, or null. `[a-z0-9._-]+` is the charset; a LEADING dot is refused on top
|
|
@@ -115,15 +116,18 @@ function withVenue(
|
|
|
115
116
|
src: string,
|
|
116
117
|
venue: VenueSetting,
|
|
117
118
|
): { ok: true; text: string } | { ok: false; reason: string } {
|
|
118
|
-
let obj:
|
|
119
|
+
let obj: unknown;
|
|
119
120
|
try {
|
|
120
|
-
obj = JSON.parse(text)
|
|
121
|
+
obj = JSON.parse(text);
|
|
121
122
|
} catch {
|
|
123
|
+
obj = null;
|
|
124
|
+
}
|
|
125
|
+
// Not JSON, or JSON that is not an object: there is no field to write "extends" into.
|
|
126
|
+
if (!isRecord(obj))
|
|
122
127
|
return {
|
|
123
128
|
ok: false,
|
|
124
129
|
reason: `--venue cannot be written into ${src}: it is not plain JSON — set "extends" by hand`,
|
|
125
130
|
};
|
|
126
|
-
}
|
|
127
131
|
const out: Record<string, unknown> = { ...obj, extends: venue.extends };
|
|
128
132
|
delete out["$comment"];
|
|
129
133
|
if (venue.kind !== null) out["kind"] = venue.kind;
|
package/src/paper-files.test.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* 74 errors — parse errors and "Definition for rule … was not found" — none of them on a paper
|
|
9
9
|
* file, and the run failed. Nothing in the settings could turn that off.
|
|
10
10
|
*/
|
|
11
|
-
import { ESLint
|
|
11
|
+
import { ESLint } from "eslint";
|
|
12
12
|
import {
|
|
13
13
|
mkdirSync,
|
|
14
14
|
mkdtempSync,
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
import { tmpdir } from "node:os";
|
|
20
20
|
import { join } from "node:path";
|
|
21
21
|
import { afterEach, describe, expect, it } from "vitest";
|
|
22
|
-
import { buildConfig, run } from "./cli.ts";
|
|
22
|
+
import { buildConfig, eslintConfig, run } from "./cli.ts";
|
|
23
23
|
import {
|
|
24
24
|
narrowToOwners,
|
|
25
25
|
ownedPatterns,
|
|
@@ -27,6 +27,11 @@ import {
|
|
|
27
27
|
ruleOwners,
|
|
28
28
|
scopeToOwned,
|
|
29
29
|
} from "./paper-files.ts";
|
|
30
|
+
import { lintReport } from "../test/lint-report.ts";
|
|
31
|
+
import { z } from "zod";
|
|
32
|
+
|
|
33
|
+
/** What `calculateConfigForFile` answers for a file inside the config's scope, read for its rules. */
|
|
34
|
+
const Computed = z.object({ rules: z.record(z.string(), z.unknown()) });
|
|
30
35
|
|
|
31
36
|
const dirs: string[] = [];
|
|
32
37
|
afterEach(() => {
|
|
@@ -67,7 +72,7 @@ async function lint(
|
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
const linted = (stdout: string): string[] =>
|
|
70
|
-
(
|
|
75
|
+
lintReport(stdout).map((r) => r.filePath);
|
|
71
76
|
|
|
72
77
|
/** Vendored code under a paper: a syntax error, an unknown rule in a directive, plain CommonJS. */
|
|
73
78
|
const VENDORED = {
|
|
@@ -208,10 +213,7 @@ describe("🔴 a generated block never reaches a file paperlint does not lint (#
|
|
|
208
213
|
async (_, settings) => {
|
|
209
214
|
const root = project({ ...SIBLINGS, ...settings });
|
|
210
215
|
const r = await lint(root);
|
|
211
|
-
const results =
|
|
212
|
-
filePath: string;
|
|
213
|
-
messages: { ruleId: string | null; message: string }[];
|
|
214
|
-
}[];
|
|
216
|
+
const results = lintReport(r.out);
|
|
215
217
|
// Guards: the class — no file reaches ESLint's default JavaScript parser.
|
|
216
218
|
expect(
|
|
217
219
|
results.flatMap((x) => x.messages).filter((m) => m.ruleId === null),
|
|
@@ -234,25 +236,24 @@ describe("`rules` blocks — where the rule lands", () => {
|
|
|
234
236
|
const eslint = new ESLint({
|
|
235
237
|
cwd: root,
|
|
236
238
|
overrideConfigFile: true,
|
|
237
|
-
overrideConfig:
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
239
|
+
overrideConfig: eslintConfig(
|
|
240
|
+
buildConfig(
|
|
241
|
+
{
|
|
242
|
+
rules: [
|
|
243
|
+
{
|
|
244
|
+
basePath: root,
|
|
245
|
+
files: ["papers/**"],
|
|
246
|
+
rules: { "paper/source": "warn", "paper/section-word": "off" },
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
null,
|
|
251
|
+
),
|
|
252
|
+
),
|
|
249
253
|
});
|
|
250
254
|
const on = async (file: string) =>
|
|
251
|
-
(
|
|
252
|
-
|
|
253
|
-
rules: Record<string, unknown>;
|
|
254
|
-
}
|
|
255
|
-
).rules;
|
|
255
|
+
Computed.parse(await eslint.calculateConfigForFile(join(root, file)))
|
|
256
|
+
.rules;
|
|
256
257
|
const status = await on("papers/a/PIPELINE-STATUS.md");
|
|
257
258
|
expect(status["paper/source"]).toEqual([1]);
|
|
258
259
|
expect(status["paper/section-word"]).toBeUndefined();
|
package/src/paper-files.ts
CHANGED
|
@@ -40,16 +40,7 @@
|
|
|
40
40
|
* - later global-ignore patterns win, across blocks, in config order;
|
|
41
41
|
* - `files: [["a", "b"]]` matches a file only when BOTH patterns match.
|
|
42
42
|
*/
|
|
43
|
-
import type { RuleBlock, RuleEntry } from "./rules-config.ts";
|
|
44
|
-
|
|
45
|
-
/** What this module reads of a config block. */
|
|
46
|
-
interface Block {
|
|
47
|
-
readonly files?: readonly unknown[];
|
|
48
|
-
readonly ignores?: readonly unknown[];
|
|
49
|
-
readonly plugins?: Readonly<
|
|
50
|
-
Record<string, { readonly rules?: Readonly<Record<string, unknown>> }>
|
|
51
|
-
>;
|
|
52
|
-
}
|
|
43
|
+
import type { ConfigBlock, RuleBlock, RuleEntry } from "./rules-config.ts";
|
|
53
44
|
|
|
54
45
|
/**
|
|
55
46
|
* A set of files paperlint lints: an own block's globs and the exceptions that block makes. A file is
|
|
@@ -99,20 +90,18 @@ const merged = (scopes: readonly OwnedScope[]): OwnedScope[] => {
|
|
|
99
90
|
* THE FILES PAPERLINT LINTS: every own block that names `files`, with its `ignores`. A block without
|
|
100
91
|
* `files` (a global ignore, or the block registering `pdf` for every file) claims nothing.
|
|
101
92
|
*/
|
|
102
|
-
export function ownedScopes(own: readonly
|
|
93
|
+
export function ownedScopes(own: readonly ConfigBlock[]): OwnedScope[] {
|
|
103
94
|
return merged(
|
|
104
|
-
own
|
|
105
|
-
.
|
|
106
|
-
|
|
107
|
-
b.files
|
|
108
|
-
|
|
109
|
-
: [{ files: globsOf(b.files), ignores: stringsOf(b.ignores) }],
|
|
110
|
-
),
|
|
95
|
+
own.flatMap((b) =>
|
|
96
|
+
b.files === undefined
|
|
97
|
+
? []
|
|
98
|
+
: [{ files: globsOf(b.files), ignores: stringsOf(b.ignores) }],
|
|
99
|
+
),
|
|
111
100
|
);
|
|
112
101
|
}
|
|
113
102
|
|
|
114
103
|
/** Every glob paperlint's own blocks lint, once, in config order. */
|
|
115
|
-
export function ownedPatterns(own: readonly
|
|
104
|
+
export function ownedPatterns(own: readonly ConfigBlock[]): string[] {
|
|
116
105
|
return [...new Set(ownedScopes(own).flatMap((s) => s.files))];
|
|
117
106
|
}
|
|
118
107
|
|
|
@@ -134,7 +123,7 @@ export function scopeToOwned(owned: readonly string[]): {
|
|
|
134
123
|
}
|
|
135
124
|
|
|
136
125
|
/** Every `<plugin>/<rule>` a block registers. */
|
|
137
|
-
const ruleIdsOf = (b:
|
|
126
|
+
const ruleIdsOf = (b: ConfigBlock): string[] =>
|
|
138
127
|
Object.entries(b.plugins ?? {}).flatMap(([name, plugin]) =>
|
|
139
128
|
Object.keys(plugin.rules ?? {}).map((rule) => `${name}/${rule}`),
|
|
140
129
|
);
|
|
@@ -145,12 +134,11 @@ const ruleIdsOf = (b: Block): string[] =>
|
|
|
145
134
|
* one file more.
|
|
146
135
|
*/
|
|
147
136
|
export function ruleOwners(
|
|
148
|
-
own: readonly
|
|
137
|
+
own: readonly ConfigBlock[],
|
|
149
138
|
): Map<string, readonly OwnedScope[]> {
|
|
150
139
|
const all = ownedScopes(own);
|
|
151
140
|
const found = new Map<string, OwnedScope[]>();
|
|
152
|
-
for (const
|
|
153
|
-
const b = raw as Block;
|
|
141
|
+
for (const b of own) {
|
|
154
142
|
const scopes =
|
|
155
143
|
b.files === undefined
|
|
156
144
|
? all
|
|
@@ -171,17 +159,18 @@ export function narrowToOwners(
|
|
|
171
159
|
block: RuleBlock,
|
|
172
160
|
owners: ReadonlyMap<string, readonly OwnedScope[]>,
|
|
173
161
|
): ScopedBlock[] {
|
|
162
|
+
// Keyed by the owner's JSON, so rules with equal owners share one group of blocks.
|
|
174
163
|
const groups = new Map<string, Record<string, RuleEntry>>();
|
|
164
|
+
const ownerOf = new Map<string, readonly OwnedScope[]>();
|
|
175
165
|
for (const [id, entry] of Object.entries(block.rules)) {
|
|
176
166
|
const owner = owners.get(id);
|
|
177
167
|
if (owner === undefined) continue;
|
|
178
168
|
const key = JSON.stringify(owner);
|
|
179
169
|
groups.set(key, { ...groups.get(key), [id]: entry });
|
|
170
|
+
ownerOf.set(key, owner);
|
|
180
171
|
}
|
|
181
|
-
return [...
|
|
182
|
-
|
|
183
|
-
within(block, scope, rules),
|
|
184
|
-
),
|
|
172
|
+
return [...ownerOf].flatMap(([key, owner]) =>
|
|
173
|
+
owner.map((scope) => within(block, scope, { ...groups.get(key) })),
|
|
185
174
|
);
|
|
186
175
|
}
|
|
187
176
|
|
|
@@ -13,6 +13,7 @@ import { tmpdir } from "node:os";
|
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
import { afterEach, describe, expect, it } from "vitest";
|
|
15
15
|
import { run, toolchainTex } from "./cli.ts";
|
|
16
|
+
import { lintReport } from "../test/lint-report.ts";
|
|
16
17
|
|
|
17
18
|
const dirs: string[] = [];
|
|
18
19
|
afterEach(() => {
|
|
@@ -55,9 +56,9 @@ async function lint(
|
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
const rulesIn = (stdout: string, paper: string): string[] =>
|
|
58
|
-
(
|
|
59
|
+
lintReport(stdout)
|
|
59
60
|
.filter((r) => r.filePath.endsWith(join(paper, "paper.tex")))
|
|
60
|
-
.flatMap((r) => r.messages.map((m) => m.ruleId));
|
|
61
|
+
.flatMap((r) => r.messages.map((m) => m.ruleId ?? "(fatal)"));
|
|
61
62
|
|
|
62
63
|
describe("🔴 an optional rule that reaches no paper — judged against the PROJECT, not the run (#103)", () => {
|
|
63
64
|
const THIRD = {
|
|
@@ -151,7 +152,7 @@ describe("papersDir — optional, `papers` by default", () => {
|
|
|
151
152
|
const r = await lint(project());
|
|
152
153
|
expect(r.code).toBe(0);
|
|
153
154
|
expect(
|
|
154
|
-
(
|
|
155
|
+
lintReport(r.out).map((f) =>
|
|
155
156
|
f.filePath.slice(f.filePath.indexOf("papers/")),
|
|
156
157
|
),
|
|
157
158
|
).toEqual(
|
|
@@ -178,7 +179,7 @@ describe("papersDir — optional, `papers` by default", () => {
|
|
|
178
179
|
"docs/drafts/c/PIPELINE-STATUS.md": "---\nstages: []\n---\n",
|
|
179
180
|
});
|
|
180
181
|
const r = await lint(root);
|
|
181
|
-
const linted = (
|
|
182
|
+
const linted = lintReport(r.out).map((f) =>
|
|
182
183
|
f.filePath.slice(root.length + 1),
|
|
183
184
|
);
|
|
184
185
|
expect(linted).toContain("docs/drafts/c/paper.tex");
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { describe, expect, it } from "vitest";
|
|
6
6
|
import { memoryFiles } from "./adapters/memory/index.ts";
|
|
7
|
-
import
|
|
8
|
-
import { findProjectRoot } from "../lib/paper-config.
|
|
7
|
+
import { absolutePath } from "./domain/paths.ts";
|
|
8
|
+
import { findProjectRoot } from "../lib/paper-config.ts";
|
|
9
9
|
import {
|
|
10
10
|
paperRules,
|
|
11
11
|
parsePaperSettings,
|
|
@@ -128,12 +128,12 @@ describe("readPaperSettings — a root paperlint.json that cannot be used", () =
|
|
|
128
128
|
readPaperSettings(withFiles({ [`${ROOT}/paperlint.json`]: root }), PAPER);
|
|
129
129
|
const notJson = broken("{ nope");
|
|
130
130
|
expect(!notJson.ok && notJson.error.kind).toBe("broken");
|
|
131
|
+
const why: unknown = expect.stringMatching(
|
|
132
|
+
/^the root paperlint\.json: .*kind/,
|
|
133
|
+
);
|
|
131
134
|
expect(broken('{"kind":3}')).toEqual({
|
|
132
135
|
ok: false,
|
|
133
|
-
error: {
|
|
134
|
-
kind: "broken",
|
|
135
|
-
why: expect.stringMatching(/^the root paperlint\.json: .*kind/),
|
|
136
|
-
},
|
|
136
|
+
error: { kind: "broken", why },
|
|
137
137
|
});
|
|
138
138
|
});
|
|
139
139
|
});
|
|
@@ -215,7 +215,7 @@ describe("findProjectRoot — where the root paperlint.json is looked for", () =
|
|
|
215
215
|
[`${PAPER}/paperlint.json`]: '{"kind":"research"}',
|
|
216
216
|
[`${ROOT}/paperlint.json`]: '{"extends":"paperlint:aisec"}',
|
|
217
217
|
});
|
|
218
|
-
const isFile = (p: string) => files.isFile(p
|
|
218
|
+
const isFile = (p: string) => files.isFile(absolutePath(p));
|
|
219
219
|
expect(findProjectRoot(PAPER, isFile)).toBe(ROOT);
|
|
220
220
|
// No root file anywhere: the package.json directory.
|
|
221
221
|
expect(findProjectRoot(PAPER, (p) => p === `${ROOT}/package.json`)).toBe(
|