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