paperlint 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +72 -0
- package/.github/workflows/ci.yml +297 -0
- package/.github/workflows/dependabot-automerge.yml +70 -0
- package/.github/workflows/pr-title.yml +59 -0
- package/.github/workflows/release.yml +54 -0
- package/CLAUDE.md +598 -0
- package/CONTRIBUTING.md +159 -0
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/action.harness.mjs +287 -0
- package/action.mutations.mjs +162 -0
- package/action.yml +138 -0
- package/bin/rpp.mjs +43 -0
- package/dist/action-ref.d.ts +12 -0
- package/dist/action-ref.d.ts.map +1 -0
- package/dist/action-ref.js +16 -0
- package/dist/action-ref.js.map +1 -0
- package/dist/adapters/banal/failure.d.ts +73 -0
- package/dist/adapters/banal/failure.d.ts.map +1 -0
- package/dist/adapters/banal/failure.js +58 -0
- package/dist/adapters/banal/failure.js.map +1 -0
- package/dist/adapters/banal/index.d.ts +17 -0
- package/dist/adapters/banal/index.d.ts.map +1 -0
- package/dist/adapters/banal/index.js +56 -0
- package/dist/adapters/banal/index.js.map +1 -0
- package/dist/adapters/banal/install.d.ts +26 -0
- package/dist/adapters/banal/install.d.ts.map +1 -0
- package/dist/adapters/banal/install.js +15 -0
- package/dist/adapters/banal/install.js.map +1 -0
- package/dist/adapters/banal/invocation.d.ts +48 -0
- package/dist/adapters/banal/invocation.d.ts.map +1 -0
- package/dist/adapters/banal/invocation.js +43 -0
- package/dist/adapters/banal/invocation.js.map +1 -0
- package/dist/adapters/banal/locate.d.ts +50 -0
- package/dist/adapters/banal/locate.d.ts.map +1 -0
- package/dist/adapters/banal/locate.js +34 -0
- package/dist/adapters/banal/locate.js.map +1 -0
- package/dist/adapters/banal/output.d.ts +27 -0
- package/dist/adapters/banal/output.d.ts.map +1 -0
- package/dist/adapters/banal/output.js +112 -0
- package/dist/adapters/banal/output.js.map +1 -0
- package/dist/adapters/banal/pin.d.ts +19 -0
- package/dist/adapters/banal/pin.d.ts.map +1 -0
- package/dist/adapters/banal/pin.js +15 -0
- package/dist/adapters/banal/pin.js.map +1 -0
- package/dist/adapters/banal/probe.d.ts +12 -0
- package/dist/adapters/banal/probe.d.ts.map +1 -0
- package/dist/adapters/banal/probe.js +27 -0
- package/dist/adapters/banal/probe.js.map +1 -0
- package/dist/adapters/banal/run.d.ts +89 -0
- package/dist/adapters/banal/run.d.ts.map +1 -0
- package/dist/adapters/banal/run.js +104 -0
- package/dist/adapters/banal/run.js.map +1 -0
- package/dist/adapters/banal/settings.d.ts +18 -0
- package/dist/adapters/banal/settings.d.ts.map +1 -0
- package/dist/adapters/banal/settings.js +29 -0
- package/dist/adapters/banal/settings.js.map +1 -0
- package/dist/adapters/banal/xml.d.ts +48 -0
- package/dist/adapters/banal/xml.d.ts.map +1 -0
- package/dist/adapters/banal/xml.js +67 -0
- package/dist/adapters/banal/xml.js.map +1 -0
- package/dist/adapters/curl/download.io.d.ts +14 -0
- package/dist/adapters/curl/download.io.d.ts.map +1 -0
- package/dist/adapters/curl/download.io.js +69 -0
- package/dist/adapters/curl/download.io.js.map +1 -0
- package/dist/adapters/curl/index.d.ts +6 -0
- package/dist/adapters/curl/index.d.ts.map +1 -0
- package/dist/adapters/curl/index.js +6 -0
- package/dist/adapters/curl/index.js.map +1 -0
- package/dist/adapters/memory/index.d.ts +43 -0
- package/dist/adapters/memory/index.d.ts.map +1 -0
- package/dist/adapters/memory/index.js +79 -0
- package/dist/adapters/memory/index.js.map +1 -0
- package/dist/adapters/node/files.io.d.ts +3 -0
- package/dist/adapters/node/files.io.d.ts.map +1 -0
- package/dist/adapters/node/files.io.js +31 -0
- package/dist/adapters/node/files.io.js.map +1 -0
- package/dist/adapters/node/host.io.d.ts +3 -0
- package/dist/adapters/node/host.io.d.ts.map +1 -0
- package/dist/adapters/node/host.io.js +14 -0
- package/dist/adapters/node/host.io.js.map +1 -0
- package/dist/adapters/node/index.d.ts +25 -0
- package/dist/adapters/node/index.d.ts.map +1 -0
- package/dist/adapters/node/index.js +14 -0
- package/dist/adapters/node/index.js.map +1 -0
- package/dist/adapters/node/process.io.d.ts +14 -0
- package/dist/adapters/node/process.io.d.ts.map +1 -0
- package/dist/adapters/node/process.io.js +41 -0
- package/dist/adapters/node/process.io.js.map +1 -0
- package/dist/adapters/node/workspace.io.d.ts +4 -0
- package/dist/adapters/node/workspace.io.d.ts.map +1 -0
- package/dist/adapters/node/workspace.io.js +33 -0
- package/dist/adapters/node/workspace.io.js.map +1 -0
- package/dist/adapters/pdfjs/fill.d.ts +42 -0
- package/dist/adapters/pdfjs/fill.d.ts.map +1 -0
- package/dist/adapters/pdfjs/fill.js +91 -0
- package/dist/adapters/pdfjs/fill.js.map +1 -0
- package/dist/build-engine.d.ts +48 -0
- package/dist/build-engine.d.ts.map +1 -0
- package/dist/build-engine.js +148 -0
- package/dist/build-engine.js.map +1 -0
- package/dist/build.d.ts +163 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +575 -0
- package/dist/build.js.map +1 -0
- package/dist/cli.d.ts +151 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +951 -0
- package/dist/cli.js.map +1 -0
- package/dist/doctor.d.ts +42 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +280 -0
- package/dist/doctor.js.map +1 -0
- package/dist/domain/geometry.d.ts +71 -0
- package/dist/domain/geometry.d.ts.map +1 -0
- package/dist/domain/geometry.js +35 -0
- package/dist/domain/geometry.js.map +1 -0
- package/dist/domain/host.d.ts +16 -0
- package/dist/domain/host.d.ts.map +1 -0
- package/dist/domain/host.js +8 -0
- package/dist/domain/host.js.map +1 -0
- package/dist/domain/page-layout.d.ts +34 -0
- package/dist/domain/page-layout.d.ts.map +1 -0
- package/dist/domain/page-layout.js +8 -0
- package/dist/domain/page-layout.js.map +1 -0
- package/dist/domain/paths.d.ts +5 -0
- package/dist/domain/paths.d.ts.map +1 -0
- package/dist/domain/paths.js +2 -0
- package/dist/domain/paths.js.map +1 -0
- package/dist/domain/result.d.ts +23 -0
- package/dist/domain/result.d.ts.map +1 -0
- package/dist/domain/result.js +10 -0
- package/dist/domain/result.js.map +1 -0
- package/dist/domain/sha256.d.ts +7 -0
- package/dist/domain/sha256.d.ts.map +1 -0
- package/dist/domain/sha256.js +14 -0
- package/dist/domain/sha256.js.map +1 -0
- package/dist/domain/text.d.ts +6 -0
- package/dist/domain/text.d.ts.map +1 -0
- package/dist/domain/text.js +7 -0
- package/dist/domain/text.js.map +1 -0
- package/dist/engine.d.ts +93 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +119 -0
- package/dist/engine.js.map +1 -0
- package/dist/exit-code.d.ts +22 -0
- package/dist/exit-code.d.ts.map +1 -0
- package/dist/exit-code.js +10 -0
- package/dist/exit-code.js.map +1 -0
- package/dist/facts-file.d.ts +96 -0
- package/dist/facts-file.d.ts.map +1 -0
- package/dist/facts-file.js +134 -0
- package/dist/facts-file.js.map +1 -0
- package/dist/hooks-settings.d.ts +141 -0
- package/dist/hooks-settings.d.ts.map +1 -0
- package/dist/hooks-settings.js +306 -0
- package/dist/hooks-settings.js.map +1 -0
- package/dist/init.d.ts +201 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +579 -0
- package/dist/init.js.map +1 -0
- package/dist/latex-log.d.ts +80 -0
- package/dist/latex-log.d.ts.map +1 -0
- package/dist/latex-log.js +187 -0
- package/dist/latex-log.js.map +1 -0
- package/dist/latex-loop.d.ts +129 -0
- package/dist/latex-loop.d.ts.map +1 -0
- package/dist/latex-loop.js +113 -0
- package/dist/latex-loop.js.map +1 -0
- package/dist/link-skills.d.ts +51 -0
- package/dist/link-skills.d.ts.map +1 -0
- package/dist/link-skills.js +199 -0
- package/dist/link-skills.js.map +1 -0
- package/dist/new-paper.d.ts +48 -0
- package/dist/new-paper.d.ts.map +1 -0
- package/dist/new-paper.js +110 -0
- package/dist/new-paper.js.map +1 -0
- package/dist/pdf-facts.d.ts +44 -0
- package/dist/pdf-facts.d.ts.map +1 -0
- package/dist/pdf-facts.js +239 -0
- package/dist/pdf-facts.js.map +1 -0
- package/dist/pdf-geometry.d.ts +170 -0
- package/dist/pdf-geometry.d.ts.map +1 -0
- package/dist/pdf-geometry.js +158 -0
- package/dist/pdf-geometry.js.map +1 -0
- package/dist/ports/download.d.ts +9 -0
- package/dist/ports/download.d.ts.map +1 -0
- package/dist/ports/download.js +2 -0
- package/dist/ports/download.js.map +1 -0
- package/dist/ports/files.d.ts +11 -0
- package/dist/ports/files.d.ts.map +1 -0
- package/dist/ports/files.js +2 -0
- package/dist/ports/files.js.map +1 -0
- package/dist/ports/measure-geometry.d.ts +8 -0
- package/dist/ports/measure-geometry.d.ts.map +1 -0
- package/dist/ports/measure-geometry.js +2 -0
- package/dist/ports/measure-geometry.js.map +1 -0
- package/dist/ports/process.d.ts +45 -0
- package/dist/ports/process.d.ts.map +1 -0
- package/dist/ports/process.js +2 -0
- package/dist/ports/process.js.map +1 -0
- package/dist/ports/tool-installer.d.ts +29 -0
- package/dist/ports/tool-installer.d.ts.map +1 -0
- package/dist/ports/tool-installer.js +2 -0
- package/dist/ports/tool-installer.js.map +1 -0
- package/dist/ports/workspace.d.ts +18 -0
- package/dist/ports/workspace.d.ts.map +1 -0
- package/dist/ports/workspace.js +2 -0
- package/dist/ports/workspace.js.map +1 -0
- package/dist/rules-config.d.ts +34 -0
- package/dist/rules-config.d.ts.map +1 -0
- package/dist/rules-config.js +132 -0
- package/dist/rules-config.js.map +1 -0
- package/dist/structure.d.ts +34 -0
- package/dist/structure.d.ts.map +1 -0
- package/dist/structure.js +149 -0
- package/dist/structure.js.map +1 -0
- package/dist/tex-requirements.d.ts +43 -0
- package/dist/tex-requirements.d.ts.map +1 -0
- package/dist/tex-requirements.js +127 -0
- package/dist/tex-requirements.js.map +1 -0
- package/dist/toolchain.d.ts +159 -0
- package/dist/toolchain.d.ts.map +1 -0
- package/dist/toolchain.js +542 -0
- package/dist/toolchain.js.map +1 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/configuration.md +235 -0
- package/docs/e2e.md +152 -0
- package/docs/incidents.md +59 -0
- package/docs/install.md +170 -0
- package/docs/optional-rules.md +107 -0
- package/docs/package-shape-options.md +262 -0
- package/docs/prior-art/README.md +76 -0
- package/docs/prior-art/blocking-vs-advisory.md +83 -0
- package/docs/prior-art/content-delivery.md +124 -0
- package/docs/prior-art/multi-mode-tools.md +106 -0
- package/docs/prior-art/nondeterministic-checks.md +99 -0
- package/docs/prior-art/package-location.md +422 -0
- package/docs/prior-art/paper-folder-scaffolding.md +538 -0
- package/docs/prior-art/readme-structure.md +69 -0
- package/docs/prior-art/repro/README.md +92 -0
- package/docs/prior-art/repro/claim1-allowedtools.mjs +66 -0
- package/docs/prior-art/repro/claim1-at2.mjs +40 -0
- package/docs/prior-art/repro/claim1-crosschannel.mjs +54 -0
- package/docs/prior-art/repro/claim1-frontmatter.mjs +76 -0
- package/docs/prior-art/repro/claim1-hook-payload-reporter.mjs +10 -0
- package/docs/prior-art/repro/claim1-plugin-frontmatter.mjs +27 -0
- package/docs/prior-art/repro/claim1-plugin-skill.mjs +52 -0
- package/docs/prior-art/repro/claim1-project-skill.mjs +81 -0
- package/docs/prior-art/repro/claim2-marketplace-flat-asclaimed.json +1 -0
- package/docs/prior-art/repro/claim2-marketplace-negative-control.json +1 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-exact.json +9 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-noversion.json +9 -0
- package/docs/prior-art/repro/claim2-marketplace-nested-range.json +1 -0
- package/docs/prior-art/repro/claim3-imports.mjs +50 -0
- package/docs/prior-art/repro/claim4-find-package-json.mjs +8 -0
- package/docs/prior-art/repro/claim4-package-dir.mjs +39 -0
- package/docs/prior-art/repro/claim4-parent-arg.mjs +17 -0
- package/docs/prior-art/repro/claim4-resolve-apis.mjs +21 -0
- package/docs/prior-art/repro/claim4-setup-consumers.mjs +45 -0
- package/docs/prior-art/repro/claim4-yarn-pnp.mjs +70 -0
- package/docs/prior-art/repro/claim5-bin-launch.mjs +39 -0
- package/docs/prior-art/repro/claim5-exports-mutation.mjs +57 -0
- package/docs/prior-art/repro/claim5-resolved-location-and-bin.mjs +33 -0
- package/docs/prior-art/repro/claim6-candidate-ambiguity.mjs +17 -0
- package/docs/prior-art/repro/claim6-doc-path-candidates.mjs +27 -0
- package/docs/prior-art/test-tooling.md +131 -0
- package/docs/rules.md +58 -0
- package/docs/texlive-install-decision.md +230 -0
- package/docs/toolchain.md +152 -0
- package/eslint-rules/doc-fields.harness.mjs +336 -0
- package/eslint-rules/doc-fields.mjs +186 -0
- package/eslint-rules/doc-fields.mutations.mjs +96 -0
- package/eslint-rules/install-path-literals.harness.mjs +121 -0
- package/eslint-rules/install-path-literals.mjs +108 -0
- package/eslint-rules/install-path-literals.mutations.mjs +62 -0
- package/eslint-rules/latex-language.harness.mjs +599 -0
- package/eslint-rules/latex-language.mjs +591 -0
- package/eslint-rules/latex-language.mutations.mjs +196 -0
- package/eslint-rules/paper-research-question.harness.mjs +146 -0
- package/eslint-rules/paper-research-question.mjs +180 -0
- package/eslint-rules/paper-research-question.mutations.mjs +127 -0
- package/eslint-rules/paper-stages.harness.mjs +356 -0
- package/eslint-rules/paper-stages.mjs +455 -0
- package/eslint-rules/paper-stages.mutations.mjs +157 -0
- package/eslint-rules/paper-typography.harness.mjs +291 -0
- package/eslint-rules/paper-typography.mjs +313 -0
- package/eslint-rules/paper-typography.mutations.mjs +131 -0
- package/eslint-rules/papers.harness.mjs +259 -0
- package/eslint-rules/papers.mjs +166 -0
- package/eslint-rules/papers.mutations.mjs +186 -0
- package/eslint-rules/pdf-last-page-balance.harness.mjs +206 -0
- package/eslint-rules/pdf-last-page-balance.mjs +208 -0
- package/eslint-rules/review-findings-cause.harness.mjs +228 -0
- package/eslint-rules/review-findings-cause.mjs +135 -0
- package/eslint-rules/review-findings-cause.mutations.mjs +72 -0
- package/eslint-rules/temp-root-realpath.harness.mjs +176 -0
- package/eslint-rules/temp-root-realpath.mjs +129 -0
- package/eslint-rules/temp-root-realpath.mutations.mjs +99 -0
- package/eslint-rules/tex-build.harness.mjs +753 -0
- package/eslint-rules/tex-build.mjs +322 -0
- package/eslint-rules/tex-build.mutations.mjs +258 -0
- package/eslint.config.mjs +521 -0
- package/fixtures/build-e2e/acmart/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/acmart/paper.tex +11 -0
- package/fixtures/build-e2e/acmart/venue.json +1 -0
- package/fixtures/build-e2e/broken/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/broken/paper.tex +7 -0
- package/fixtures/build-e2e/cite/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/cite/build.sh +5 -0
- package/fixtures/build-e2e/cite/paper.tex +10 -0
- package/fixtures/build-e2e/cite/refs.bib +9 -0
- package/fixtures/build-e2e/empty/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/empty/paper.tex +6 -0
- package/fixtures/build-e2e/fallback/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/fallback/paper.tex +11 -0
- package/fixtures/build-e2e/guards/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/guards/paper.tex +10 -0
- package/fixtures/build-e2e/no-source/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/unbalanced/PIPELINE-STATUS.md +3 -0
- package/fixtures/build-e2e/unbalanced/paper.tex +28 -0
- package/fixtures/build-e2e/unbalanced/refs.bib +269 -0
- package/fixtures/install-path-literals/clean.fixture.mjs +3 -0
- package/fixtures/install-path-literals/clean.md +15 -0
- package/fixtures/install-path-literals/defect.fixture.mjs +3 -0
- package/fixtures/install-path-literals/defect.md +14 -0
- package/fixtures/latex-language/clean.tex +50 -0
- package/fixtures/latex-language/defect.tex +52 -0
- package/fixtures/paper-research-question/comment-only/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-research-question/comment-only/paper.tex +7 -0
- package/fixtures/paper-research-question/declared-not-in-paper/PIPELINE-STATUS.md +10 -0
- package/fixtures/paper-research-question/declared-not-in-paper/paper.tex +6 -0
- package/fixtures/paper-research-question/draft/PIPELINE-STATUS.md +6 -0
- package/fixtures/paper-research-question/draft/paper.tex +2 -0
- package/fixtures/paper-research-question/markdown-no-rq/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-research-question/markdown-no-rq/paper.md +4 -0
- package/fixtures/paper-research-question/shipped-no-rq/PIPELINE-STATUS.md +12 -0
- package/fixtures/paper-research-question/shipped-no-rq/paper.tex +3 -0
- package/fixtures/paper-research-question/shipped-with-rq/PIPELINE-STATUS.md +10 -0
- package/fixtures/paper-research-question/shipped-with-rq/paper.tex +2 -0
- package/fixtures/paper-stages/authors-ran/PIPELINE-STATUS.md +16 -0
- package/fixtures/paper-stages/marker-in-prose/PIPELINE-STATUS.md +17 -0
- package/fixtures/paper-stages/nofile/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/noheader/PIPELINE-STATUS.md +1 -0
- package/fixtures/paper-stages/noheader/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/nothing/PIPELINE-STATUS.md +3 -0
- package/fixtures/paper-stages/ok/PIPELINE-STATUS.md +9 -0
- package/fixtures/paper-stages/ok/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/stale/PIPELINE-STATUS.md +1 -0
- package/fixtures/paper-stages/stale/versions/2026-07-22-submitted.STALE-WRONG-FILE.pdf +0 -0
- package/fixtures/paper-stages/twice/PIPELINE-STATUS.md +14 -0
- package/fixtures/paper-stages/twice/versions/2026-08-06-submitted.pdf +0 -0
- package/fixtures/paper-stages/twice/versions/2026-10-24-submitted.pdf +0 -0
- package/fixtures/paper-stages/undeclared/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/undeclared/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-stages/undeclared/versions/2026-08-29-camera-ready.pdf +0 -0
- package/fixtures/paper-stages/wrongsize/PIPELINE-STATUS.md +8 -0
- package/fixtures/paper-stages/wrongsize/versions/2026-07-22-submitted.pdf +0 -0
- package/fixtures/paper-typography/clean-paper/paper.tex +29 -0
- package/fixtures/paper-typography/messy-paper/paper.tex +27 -0
- package/fixtures/pdf-facts/README.md +22 -0
- package/fixtures/pdf-facts/corrupt-font.pdf +0 -0
- package/fixtures/pdf-facts/encrypted.pdf +0 -0
- package/fixtures/pdf-facts/hidden-text.pdf +0 -0
- package/fixtures/pdf-facts/hidden-text.tex +28 -0
- package/fixtures/pdf-facts/t3-all.pdf +0 -0
- package/fixtures/pdf-facts/t3-all.tex +8 -0
- package/fixtures/pdf-facts/t3-mixed.pdf +0 -0
- package/fixtures/pdf-facts/t3-mixed.tex +9 -0
- package/fixtures/pdf-facts/ttf.pdf +2240 -1
- package/fixtures/pdf-facts/ttf.tex +6 -0
- package/fixtures/real-markdown-paper/baseline.json +24 -0
- package/fixtures/real-markdown-paper/baseline.mjs +48 -0
- package/fixtures/render-paper/build-clean.sh +25 -0
- package/fixtures/render-paper/build-defect.sh +15 -0
- package/fixtures/review-findings-cause/clean.md +17 -0
- package/fixtures/review-findings-cause/defect.md +14 -0
- package/fixtures/review-findings-cause/old-debt.md +14 -0
- package/fixtures/review-findings-cause/quiet-in-fence.md +16 -0
- package/fixtures/tex-build/clean.tex +21 -0
- package/fixtures/tex-build/defect.tex +24 -0
- package/fixtures/tex-build/frontmatter-clean.tex +25 -0
- package/fixtures/tex-build/frontmatter-defect.tex +23 -0
- package/fixtures/toolchain-mirror/catalog.txt +5 -0
- package/fixtures/toolchain-mirror/install-tl +27 -0
- package/fixtures/toolchain-mirror/release-texlive.txt +3 -0
- package/fixtures/toolchain-mirror/release-year +1 -0
- package/fixtures/toolchain-mirror/stub-kpsewhich +8 -0
- package/fixtures/toolchain-mirror/stub-pdflatex +3 -0
- package/fixtures/toolchain-mirror/stub-tlmgr +44 -0
- package/hooks/hooks.harness.mjs +713 -0
- package/hooks/hooks.mutations.mjs +337 -0
- package/hooks/paper-edit-guard.hook.d.mts +13 -0
- package/hooks/paper-edit-guard.hook.mjs +457 -0
- package/hooks/paper-skills-nudge.hook.mjs +136 -0
- package/hooks/paper-status-gates.hook.mjs +156 -0
- package/hooks/paper-status-gates.sh +91 -0
- package/lib/agent-cli-version.harness.mjs +165 -0
- package/lib/agent-cli-version.mjs +106 -0
- package/lib/agent-cli-version.mutations.mjs +109 -0
- package/lib/markdown.mjs +386 -0
- package/lib/mutation-driver.harness.mjs +227 -0
- package/lib/mutation-driver.mjs +397 -0
- package/lib/mutation-driver.mutations.mjs +68 -0
- package/lib/paper-config.d.mts +34 -0
- package/lib/paper-config.harness.mjs +286 -0
- package/lib/paper-config.mjs +142 -0
- package/lib/paper-config.mutations.mjs +143 -0
- package/lib/skill-checks.mjs +701 -0
- package/lib/skill-corpus.mjs +403 -0
- package/lib/skill-eval-fixture.mjs +63 -0
- package/lib/skill-eval-kit.mjs +257 -0
- package/lib/skill-trigger-cases.harness.mjs +170 -0
- package/lib/skill-trigger-cases.mjs +446 -0
- package/lib/skill-trigger-cases.mutations.mjs +65 -0
- package/lib/trigger-ledger.mjs +215 -0
- package/package.json +97 -0
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/hooks/hooks.json +30 -0
- package/scripts/check.harness.mjs +177 -0
- package/scripts/check.mjs +239 -0
- package/scripts/check.mutations.mjs +110 -0
- package/scripts/eslint-report-guard.mjs +82 -0
- package/scripts/exclusive.mjs +138 -0
- package/scripts/harness-api.frozen.json +76 -0
- package/scripts/harness-api.test.ts +175 -0
- package/scripts/layer-legacy-frozen.d.mts +28 -0
- package/scripts/layer-legacy-frozen.mjs +152 -0
- package/scripts/layer-legacy-frozen.test.ts +115 -0
- package/scripts/layer-legacy.frozen.json +50 -0
- package/scripts/mutation-batteries-frozen.harness.mjs +204 -0
- package/scripts/mutation-batteries-frozen.mjs +238 -0
- package/scripts/mutation-batteries.frozen.json +117 -0
- package/scripts/release-config.test.ts +90 -0
- package/scripts/rules-are-content-only.harness.mjs +113 -0
- package/scripts/rules-are-content-only.mjs +138 -0
- package/scripts/rules-are-content-only.mutations.mjs +81 -0
- package/scripts/rules-see-files.harness.mjs +115 -0
- package/scripts/rules-see-files.mjs +99 -0
- package/scripts/rules-see-files.mutations.mjs +131 -0
- package/scripts/run-mutations.mjs +100 -0
- package/scripts/semantic-release-plugins.d.ts +16 -0
- package/skills/README.md +15 -0
- package/skills/analyze-sibling-paper/SKILL.md +170 -0
- package/skills/analyze-sibling-paper/SKILL.md.spec.ts +186 -0
- package/skills/analyze-sibling-paper/analyze-sibling-paper.eval.mjs +19 -0
- package/skills/analyze-sibling-paper/analyze-sibling-paper.harness.mjs +23 -0
- package/skills/argument-arc/SKILL.md +177 -0
- package/skills/argument-arc/SKILL.md.spec.ts +192 -0
- package/skills/argument-arc/argument-arc.eval.mjs +19 -0
- package/skills/argument-arc/argument-arc.harness.mjs +23 -0
- package/skills/build-benchmark/SKILL.md +213 -0
- package/skills/build-benchmark/SKILL.md.spec.ts +220 -0
- package/skills/build-benchmark/build-benchmark.eval.mjs +19 -0
- package/skills/build-benchmark/build-benchmark.harness.mjs +23 -0
- package/skills/build-benchmark/references/adversarial-cold-repro.md +68 -0
- package/skills/camera-ready/SKILL.md +148 -0
- package/skills/camera-ready/SKILL.md.spec.ts +164 -0
- package/skills/camera-ready/camera-ready.eval.mjs +19 -0
- package/skills/camera-ready/camera-ready.harness.mjs +23 -0
- package/skills/cold-read-diff/SKILL.md +160 -0
- package/skills/cold-read-diff/SKILL.md.spec.ts +166 -0
- package/skills/cold-read-diff/cold-read-diff.eval.mjs +19 -0
- package/skills/cold-read-diff/cold-read-diff.harness.mjs +23 -0
- package/skills/draft-paper/SKILL.md +152 -0
- package/skills/draft-paper/SKILL.md.spec.ts +169 -0
- package/skills/draft-paper/draft-paper.eval.mjs +19 -0
- package/skills/draft-paper/draft-paper.harness.mjs +23 -0
- package/skills/extend-paper/SKILL.md +99 -0
- package/skills/extend-paper/SKILL.md.spec.ts +116 -0
- package/skills/extend-paper/extend-paper.eval.mjs +19 -0
- package/skills/extend-paper/extend-paper.harness.mjs +23 -0
- package/skills/find-venue/SKILL.md +128 -0
- package/skills/find-venue/SKILL.md.spec.ts +145 -0
- package/skills/find-venue/find-venue.eval.mjs +19 -0
- package/skills/find-venue/find-venue.harness.mjs +23 -0
- package/skills/grade-paper-writing/SKILL.md +436 -0
- package/skills/grade-paper-writing/SKILL.md.spec.ts +453 -0
- package/skills/grade-paper-writing/fixtures/control_gopen.txt +1 -0
- package/skills/grade-paper-writing/fixtures/control_human_paper.txt +1 -0
- package/skills/grade-paper-writing/fixtures/rewrite.txt +1 -0
- package/skills/grade-paper-writing/fixtures/specimen.txt +1 -0
- package/skills/grade-paper-writing/fixtures/structure-checks.md +22 -0
- package/skills/grade-paper-writing/grade-paper-writing.eval.mjs +19 -0
- package/skills/grade-paper-writing/grade-paper-writing.harness.mjs +23 -0
- package/skills/grade-paper-writing/prose-lint.mjs +713 -0
- package/skills/harden-paper/SKILL.md +318 -0
- package/skills/harden-paper/SKILL.md.spec.ts +336 -0
- package/skills/harden-paper/check-numbers.sh +33 -0
- package/skills/harden-paper/check-release-claims.sh +35 -0
- package/skills/harden-paper/fixtures/uncited-assertions-sample.md +43 -0
- package/skills/harden-paper/fixtures/uncited-assertions-sample.tex +77 -0
- package/skills/harden-paper/harden-paper.eval.mjs +19 -0
- package/skills/harden-paper/harden-paper.harness.mjs +23 -0
- package/skills/map-prior-work/SKILL.md +211 -0
- package/skills/map-prior-work/SKILL.md.spec.ts +227 -0
- package/skills/map-prior-work/map-prior-work.eval.mjs +19 -0
- package/skills/map-prior-work/map-prior-work.harness.mjs +23 -0
- package/skills/osf-artifact-upload/SKILL.md +52 -0
- package/skills/osf-artifact-upload/SKILL.md.spec.ts +59 -0
- package/skills/osf-artifact-upload/osf-artifact-upload.eval.mjs +22 -0
- package/skills/osf-artifact-upload/osf-artifact-upload.harness.mjs +103 -0
- package/skills/paper-adversarial-review/SKILL.md +126 -0
- package/skills/paper-adversarial-review/SKILL.md.spec.ts +142 -0
- package/skills/paper-adversarial-review/paper-adversarial-review.eval.mjs +19 -0
- package/skills/paper-adversarial-review/paper-adversarial-review.harness.mjs +23 -0
- package/skills/paper-pipeline/PIPELINE-MAP.md +371 -0
- package/skills/paper-pipeline/SKILL.md +499 -0
- package/skills/paper-pipeline/SKILL.md.spec.ts +517 -0
- package/skills/paper-pipeline/description-language.eval.mjs +347 -0
- package/skills/paper-pipeline/framing-vs-vocabulary.eval.mjs +891 -0
- package/skills/paper-pipeline/grade-paper-writing-ablation.eval.mjs +1254 -0
- package/skills/paper-pipeline/paper-pipeline.eval.mjs +22 -0
- package/skills/paper-pipeline/paper-pipeline.harness.mjs +143 -0
- package/skills/paper-pipeline/pipeline-firing.baseline.json +270 -0
- package/skills/paper-pipeline/pipeline-firing.eval.mjs +664 -0
- package/skills/paper-pipeline/pipeline-language.eval.mjs +672 -0
- package/skills/paper-pipeline/references/acceptance-gate.md +329 -0
- package/skills/paper-pipeline/references/acl-venue-rules.md +142 -0
- package/skills/paper-pipeline/references/anonymization.md +68 -0
- package/skills/paper-pipeline/references/artifact-checklist.md +93 -0
- package/skills/paper-pipeline/references/body-vs-appendix.md +97 -0
- package/skills/paper-pipeline/references/credit-criteria.md +69 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/README.md +35 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/run_retext.mjs +24 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/sentences.txt +11 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-probe/test_sentences.py +25 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-prose-checkers.md +538 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-reproducible-tooling.md +431 -0
- package/skills/paper-pipeline/references/occupancy-2026-08-06-staleness-and-orchestration.md +592 -0
- package/skills/paper-pipeline/references/pipeline-status-template.md +162 -0
- package/skills/paper-pipeline/references/review-ratchet.md +36 -0
- package/skills/paper-pipeline/references/sweep-2026-08-09-ideal-pipeline.md +585 -0
- package/skills/paper-pipeline/references/writing-craft.md +448 -0
- package/skills/paper-pipeline/repro/2026-08-07-description-language-control.log +63 -0
- package/skills/paper-pipeline/repro/2026-08-07-fork-check.log +52 -0
- package/skills/paper-pipeline/repro/2026-08-07-fork-check2.log +33 -0
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-pilot.log +33 -0
- package/skills/paper-pipeline/repro/2026-08-07-language-eval-raw.log +166 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.json +338 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-oracle.log +118 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary-raw.log +245 -0
- package/skills/paper-pipeline/repro/2026-08-08-framing-vs-vocabulary.json +776 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-oracle.log +53 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-A6-raw.log +89 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.json +450 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-oracle.log +136 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-raw.log +242 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation-setupdiff.log +59 -0
- package/skills/paper-pipeline/repro/2026-08-08-grade-paper-writing-ablation.json +1032 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.json +139 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication-gpw.log +98 -0
- package/skills/paper-pipeline/repro/2026-08-08-parent-replication.mjs +92 -0
- package/skills/paper-pipeline/repro/README.md +129 -0
- package/skills/paper-pipeline/repro/analyze-language-eval.py +116 -0
- package/skills/paper-pipeline/scripts/README.md +344 -0
- package/skills/paper-pipeline/scripts/announce.mjs +67 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.harness.mjs +496 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.mjs +397 -0
- package/skills/paper-pipeline/scripts/artifact-coverage.mutations.mjs +218 -0
- package/skills/paper-pipeline/scripts/check-provenance.mjs +184 -0
- package/skills/paper-pipeline/scripts/consumer.d.mts +32 -0
- package/skills/paper-pipeline/scripts/consumer.harness.mjs +562 -0
- package/skills/paper-pipeline/scripts/consumer.mjs +535 -0
- package/skills/paper-pipeline/scripts/consumer.mutations.mjs +190 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.harness.mjs +457 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.mjs +656 -0
- package/skills/paper-pipeline/scripts/extract-ref-facts.mutations.mjs +54 -0
- package/skills/paper-pipeline/scripts/fixtures/clean/PIPELINE-STATUS.md +51 -0
- package/skills/paper-pipeline/scripts/fixtures/dirty/PIPELINE-STATUS.md +52 -0
- package/skills/paper-pipeline/scripts/fixtures/dirty/paper.md +6 -0
- package/skills/paper-pipeline/scripts/fixtures/real-bib/refs.bib +153 -0
- package/skills/paper-pipeline/scripts/generated-code.harness.mjs +466 -0
- package/skills/paper-pipeline/scripts/generated-code.mjs +338 -0
- package/skills/paper-pipeline/scripts/generated-code.mutations.mjs +254 -0
- package/skills/paper-pipeline/scripts/ledger.mjs +623 -0
- package/skills/paper-pipeline/scripts/ledger.selftest.mjs +286 -0
- package/skills/paper-pipeline/scripts/pipeline-check.harness.mjs +389 -0
- package/skills/paper-pipeline/scripts/pipeline-check.mjs +737 -0
- package/skills/paper-pipeline/scripts/pipeline-check.mutations.mjs +54 -0
- package/skills/paper-pipeline/scripts/pipeline-edges.mjs +169 -0
- package/skills/paper-pipeline/scripts/population-map.harness.mjs +178 -0
- package/skills/paper-pipeline/scripts/population-map.mjs +181 -0
- package/skills/paper-pipeline/scripts/population-map.mutations.mjs +65 -0
- package/skills/paper-pipeline/scripts/population-map.selftest.mjs +122 -0
- package/skills/paper-pipeline/scripts/provenance.harness.mjs +240 -0
- package/skills/paper-pipeline/scripts/provenance.mutations.mjs +59 -0
- package/skills/paper-pipeline/scripts/round-diff.harness.mjs +881 -0
- package/skills/paper-pipeline/scripts/round-diff.mjs +576 -0
- package/skills/paper-pipeline/scripts/round-diff.mutations.mjs +276 -0
- package/skills/paper-pipeline/scripts/run-mechanical.mjs +633 -0
- package/skills/paper-pipeline/scripts/status.mjs +295 -0
- package/skills/paper-status/SKILL.md +183 -0
- package/skills/paper-status/SKILL.md.spec.ts +190 -0
- package/skills/paper-status/paper-status.eval.mjs +22 -0
- package/skills/paper-status/paper-status.harness.mjs +25 -0
- package/skills/pc-panel-review/SKILL.md +263 -0
- package/skills/pc-panel-review/SKILL.md.spec.ts +280 -0
- package/skills/pc-panel-review/pc-panel-review.eval.mjs +19 -0
- package/skills/pc-panel-review/pc-panel-review.harness.mjs +23 -0
- package/skills/plan-paper-timeline/SKILL.md +182 -0
- package/skills/plan-paper-timeline/SKILL.md.spec.ts +200 -0
- package/skills/plan-paper-timeline/fixtures/fake-google-calendar.mjs +239 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.harness.mjs +431 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.effects.mutations.mjs +65 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.eval.mjs +19 -0
- package/skills/plan-paper-timeline/plan-paper-timeline.harness.mjs +23 -0
- package/skills/render-paper/SKILL.md +159 -0
- package/skills/render-paper/SKILL.md.spec.ts +166 -0
- package/skills/render-paper/check-render.sh +419 -0
- package/skills/render-paper/checkers-requirements.txt +55 -0
- package/skills/render-paper/ensure-checkers.sh +69 -0
- package/skills/render-paper/extract-pdf-facts.harness.mjs +166 -0
- package/skills/render-paper/extract-pdf-facts.mjs +144 -0
- package/skills/render-paper/render-paper.eval.mjs +19 -0
- package/skills/render-paper/render-paper.harness.mjs +339 -0
- package/skills/research-ideate/SKILL.md +136 -0
- package/skills/research-ideate/SKILL.md.spec.ts +152 -0
- package/skills/research-ideate/research-ideate.eval.mjs +19 -0
- package/skills/research-ideate/research-ideate.harness.mjs +23 -0
- package/skills/skill-contract.mutations.mjs +179 -0
- package/skills/study-accepted-papers/SKILL.md +206 -0
- package/skills/study-accepted-papers/SKILL.md.spec.ts +223 -0
- package/skills/study-accepted-papers/study-accepted-papers.eval.mjs +19 -0
- package/skills/study-accepted-papers/study-accepted-papers.harness.mjs +23 -0
- package/skills/submit-paper/SKILL.md +182 -0
- package/skills/submit-paper/SKILL.md.spec.ts +199 -0
- package/skills/submit-paper/check-deanon.sh +149 -0
- package/skills/submit-paper/references/publishers/acm.md +92 -0
- package/skills/submit-paper/references/venues/agenticdev.jsonc +108 -0
- package/skills/submit-paper/references/venues/agenticdev.md +139 -0
- package/skills/submit-paper/references/venues/agenticdev.tex +19 -0
- package/skills/submit-paper/references/venues/aisec.jsonc +101 -0
- package/skills/submit-paper/references/venues/aisec.md +105 -0
- package/skills/submit-paper/references/venues/paper-guards.tex +41 -0
- package/skills/submit-paper/references/venues/realm.jsonc +81 -0
- package/skills/submit-paper/references/venues/realm.md +155 -0
- package/skills/submit-paper/references/venues/tex-base.jsonc +50 -0
- package/skills/submit-paper/references/venues/venue-profile.schema.json +74 -0
- package/skills/submit-paper/submit-paper.eval.mjs +19 -0
- package/skills/submit-paper/submit-paper.harness.mjs +23 -0
- package/skills/sweep-design-space/SKILL.md +269 -0
- package/skills/sweep-design-space/SKILL.md.spec.ts +285 -0
- package/skills/sweep-design-space/sweep-design-space.eval.mjs +19 -0
- package/skills/sweep-design-space/sweep-design-space.harness.mjs +23 -0
- package/skills/tighten-paper/SKILL.md +368 -0
- package/skills/tighten-paper/SKILL.md.spec.ts +384 -0
- package/skills/tighten-paper/structure.mjs +371 -0
- package/skills/tighten-paper/tighten-paper.eval.mjs +19 -0
- package/skills/tighten-paper/tighten-paper.harness.mjs +23 -0
- package/skills/verify-citations/SKILL.md +328 -0
- package/skills/verify-citations/SKILL.md.spec.ts +345 -0
- package/skills/verify-citations/scripts/bib-authors.mjs +479 -0
- package/skills/verify-citations/scripts/bib-authors.test.mjs +175 -0
- package/skills/verify-citations/scripts/verify-cites.mjs +1108 -0
- package/skills/verify-citations/scripts/verify-cites.test.mjs +735 -0
- package/skills/verify-citations/verify-citations.eval.mjs +19 -0
- package/skills/verify-citations/verify-citations.harness.mjs +23 -0
- package/src/CLAUDE.md +51 -0
- package/src/action-ref.test.ts +26 -0
- package/src/action-ref.ts +15 -0
- package/src/adapters/banal/failure.test.ts +63 -0
- package/src/adapters/banal/failure.ts +118 -0
- package/src/adapters/banal/index.test.ts +119 -0
- package/src/adapters/banal/index.ts +100 -0
- package/src/adapters/banal/install.test.ts +20 -0
- package/src/adapters/banal/install.ts +41 -0
- package/src/adapters/banal/invocation.test.ts +74 -0
- package/src/adapters/banal/invocation.ts +95 -0
- package/src/adapters/banal/locate.test.ts +52 -0
- package/src/adapters/banal/locate.ts +84 -0
- package/src/adapters/banal/output.test.ts +140 -0
- package/src/adapters/banal/output.ts +141 -0
- package/src/adapters/banal/pin.ts +30 -0
- package/src/adapters/banal/probe.ts +35 -0
- package/src/adapters/banal/run.test.ts +191 -0
- package/src/adapters/banal/run.ts +244 -0
- package/src/adapters/banal/settings.test.ts +31 -0
- package/src/adapters/banal/settings.ts +55 -0
- package/src/adapters/banal/xml.test.ts +111 -0
- package/src/adapters/banal/xml.ts +112 -0
- package/src/adapters/curl/download.io.ts +73 -0
- package/src/adapters/curl/download.test.ts +55 -0
- package/src/adapters/curl/index.ts +5 -0
- package/src/adapters/memory/index.ts +131 -0
- package/src/adapters/node/files.io.ts +39 -0
- package/src/adapters/node/files.test.ts +28 -0
- package/src/adapters/node/host.io.ts +15 -0
- package/src/adapters/node/index.ts +36 -0
- package/src/adapters/node/process.io.ts +49 -0
- package/src/adapters/node/process.test.ts +46 -0
- package/src/adapters/node/workspace.io.ts +40 -0
- package/src/adapters/node/workspace.test.ts +58 -0
- package/src/adapters/pdfjs/fill.test.ts +111 -0
- package/src/adapters/pdfjs/fill.ts +141 -0
- package/src/build-engine.harness.mjs +314 -0
- package/src/build-engine.ts +219 -0
- package/src/build.harness.mjs +631 -0
- package/src/build.mutations.mjs +195 -0
- package/src/build.ts +793 -0
- package/src/cli.harness.mjs +2007 -0
- package/src/cli.mutations.mjs +448 -0
- package/src/cli.ts +1189 -0
- package/src/doctor.harness.mjs +396 -0
- package/src/doctor.mutations.mjs +175 -0
- package/src/doctor.ts +356 -0
- package/src/domain/geometry.ts +108 -0
- package/src/domain/host.ts +23 -0
- package/src/domain/page-layout.ts +32 -0
- package/src/domain/paths.ts +5 -0
- package/src/domain/result.test.ts +26 -0
- package/src/domain/result.ts +29 -0
- package/src/domain/sha256.test.ts +12 -0
- package/src/domain/sha256.ts +21 -0
- package/src/domain/text.ts +11 -0
- package/src/engine.harness.mjs +252 -0
- package/src/engine.ts +176 -0
- package/src/exit-code.test.ts +21 -0
- package/src/exit-code.ts +38 -0
- package/src/facts-file.test.ts +240 -0
- package/src/facts-file.ts +241 -0
- package/src/hooks-settings.harness.mjs +386 -0
- package/src/hooks-settings.mutations.mjs +116 -0
- package/src/hooks-settings.ts +434 -0
- package/src/init.ts +900 -0
- package/src/latex-log.harness.mjs +226 -0
- package/src/latex-log.ts +234 -0
- package/src/latex-loop.harness.mjs +449 -0
- package/src/latex-loop.ts +211 -0
- package/src/link-skills.harness.mjs +273 -0
- package/src/link-skills.mutations.mjs +136 -0
- package/src/link-skills.ts +258 -0
- package/src/new-paper.harness.mjs +216 -0
- package/src/new-paper.mutations.mjs +79 -0
- package/src/new-paper.ts +158 -0
- package/src/pdf-facts.harness.mjs +188 -0
- package/src/pdf-facts.ts +327 -0
- package/src/pdf-geometry.harness.mjs +254 -0
- package/src/pdf-geometry.ts +300 -0
- package/src/ports/download.ts +10 -0
- package/src/ports/files.ts +11 -0
- package/src/ports/measure-geometry.ts +8 -0
- package/src/ports/process.ts +46 -0
- package/src/ports/tool-installer.ts +33 -0
- package/src/ports/workspace.ts +20 -0
- package/src/rules-config.harness.mjs +114 -0
- package/src/rules-config.ts +178 -0
- package/src/structure.harness.mjs +179 -0
- package/src/structure.mutations.mjs +83 -0
- package/src/structure.ts +166 -0
- package/src/tex-requirements.harness.mjs +238 -0
- package/src/tex-requirements.ts +181 -0
- package/src/toolchain.harness.mjs +651 -0
- package/src/toolchain.ts +755 -0
- package/src/types.ts +106 -0
- package/templates/paper/PIPELINE-STATUS.md +72 -0
- package/templates/paper/paper.md +4 -0
- package/templates/paper/paper.tex +8 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
# How a package finds its own installed files, and its own bin
|
|
2
|
+
|
|
3
|
+
**Question this file answers:** `test/e2e/install.mjs` inspects the tree it just installed by
|
|
4
|
+
spelling the layout out — `node_modules/research-paper-pipeline` (line 172),
|
|
5
|
+
`node_modules/.bin/rpp` (line 276), and `node_modules/.../plugin/hooks/hooks.json` (line 135).
|
|
6
|
+
`contentDelivery()` separately tries THREE candidate base directories for a path written as prose
|
|
7
|
+
inside a skill. Both are guesses. What is the supported way, and does using it buy anything?
|
|
8
|
+
|
|
9
|
+
**Verdict, in two parts, because the two hardcodes are not the same kind of thing:**
|
|
10
|
+
|
|
11
|
+
1. **The package directory: replace the three spellings with ONE resolved anchor** —
|
|
12
|
+
`dirname(createRequire(pathToFileURL(join(consumer, "__placeholder__.js")).href)
|
|
13
|
+
.resolve("research-paper-pipeline/package.json"))`. Not because the hardcode is currently
|
|
14
|
+
wrong: it is not, under either manager measured. Because resolution additionally _asserts the
|
|
15
|
+
package is reachable by name from the consumer_, which is what the README's documented import
|
|
16
|
+
depends on and which `existsSync` cannot see — demonstrated by a mutation below.
|
|
17
|
+
2. **The `.bin` launch: keep it.** `node_modules/.bin/rpp` is not the script guessing where a
|
|
18
|
+
file is; it is the script checking that the manager created the shim, which is a real property
|
|
19
|
+
of the install and the one thing resolution cannot tell you. Read the bin's _real_ path from
|
|
20
|
+
the resolved manifest as well, and launch both — they answer different questions.
|
|
21
|
+
|
|
22
|
+
**Yarn PnP is unaffected by any of this**, and for a harder reason than
|
|
23
|
+
[`test-tooling.md`](test-tooling.md) currently records. See § "Correcting the PnP note".
|
|
24
|
+
|
|
25
|
+
**The doc-relative base (`contentDelivery`): declare it, do not add a fourth candidate.** One of
|
|
26
|
+
the three candidates is measurably dead (0 hits of 104). The other two are disjoint by spelling,
|
|
27
|
+
so today the ladder hides nothing — but the package already declares this base once, in
|
|
28
|
+
`.claude-plugin/plugin.json` (`"skills": "./skills/"`), and the platform exposes
|
|
29
|
+
`${CLAUDE_PLUGIN_ROOT}` for exactly this.
|
|
30
|
+
|
|
31
|
+
Everything below was measured on this machine on 2026-09-19: Node v22.22.2 (CI pins
|
|
32
|
+
`node-version: 22`, `.github/workflows/ci.yml`), npm 10.9.7, pnpm 10.33.0, Yarn 4.9.2 via
|
|
33
|
+
corepack. Scripts: [`repro/`](repro/README.md), CLAIM 4–6.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 1. The three APIs, and which one this script can use
|
|
38
|
+
|
|
39
|
+
| API | available on Node 22 | anchored at | our package | `exports`-closed package |
|
|
40
|
+
| -------------------------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------- | ------------------------------------------ | ---------------------------------- |
|
|
41
|
+
| `createRequire(url).resolve("<pkg>/package.json")` | yes, since v12.2.0, stable | **the URL you pass** | ✅ npm + pnpm | ❌ `ERR_PACKAGE_PATH_NOT_EXPORTED` |
|
|
42
|
+
| `require.resolve(spec, { paths: [dir] })` | yes, stable | the `paths` array | ✅ npm + pnpm | ❌ same |
|
|
43
|
+
| `import.meta.resolve(spec)` | yes — sync and unflagged since v20.6.0/v18.19.0, Stability **1.2 Release candidate** | **the calling file, always** | ✅ only if the caller sits in the consumer | ❌ same |
|
|
44
|
+
| `import.meta.resolve(spec, parent)` | **flagged** — `--experimental-import-meta-resolve` | the `parent` you pass | — | — |
|
|
45
|
+
| `module.findPackageJSON(spec, base)` | v22.14.0, Stability **1.1 Active development** | the `base` you pass | ✅ npm + pnpm | ✅ **ignores `exports`** |
|
|
46
|
+
|
|
47
|
+
Sources for the version and stability columns, fetched 2026-09-19:
|
|
48
|
+
<https://nodejs.org/docs/latest-v22.x/api/module.html> ·
|
|
49
|
+
<https://nodejs.org/docs/latest-v22.x/api/esm.html>. The Node docs are explicit that the second
|
|
50
|
+
argument of `import.meta.resolve` is the non-standard part:
|
|
51
|
+
|
|
52
|
+
> **v20.6.0, v18.19.0** — No longer behind `--experimental-import-meta-resolve` CLI flag, except
|
|
53
|
+
> for the non-standard `parentURL` parameter
|
|
54
|
+
|
|
55
|
+
### 🔴 `import.meta.resolve` is the wrong tool here, and it fails SILENTLY
|
|
56
|
+
|
|
57
|
+
`test/e2e/install.mjs` runs from the repository and inspects a consumer somewhere in `os.tmpdir()`.
|
|
58
|
+
`import.meta.resolve` always resolves from **the file that calls it** — `cwd` is irrelevant and
|
|
59
|
+
the `parent` argument is ignored without the flag rather than rejected:
|
|
60
|
+
|
|
61
|
+
```console
|
|
62
|
+
$ node docs/prior-art/repro/claim4-parent-arg.mjs <WORK>/consumer-npm
|
|
63
|
+
running from: file:///home/user/research-paper-pipeline/docs/prior-art/repro/claim4-parent-arg.mjs
|
|
64
|
+
1-arg FAIL: ERR_MODULE_NOT_FOUND Cannot find package 'research-paper-pipeline' imported from …/claim4-parent-arg.mjs
|
|
65
|
+
2-arg FAIL: ERR_MODULE_NOT_FOUND Cannot find package 'research-paper-pipeline' imported from …/claim4-parent-arg.mjs
|
|
66
|
+
require.resolve(..., {paths:[consumer]}) -> <WORK>/consumer-npm/node_modules/research-paper-pipeline/package.json
|
|
67
|
+
createRequire(consumer/x.js).resolve(...) -> <WORK>/consumer-npm/node_modules/research-paper-pipeline/package.json
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Note the second line: the parent URL pointed at the consumer, and the error still names the
|
|
71
|
+
_script's own path_. With the flag, the same line answers correctly:
|
|
72
|
+
|
|
73
|
+
```console
|
|
74
|
+
$ node --experimental-import-meta-resolve docs/prior-art/repro/claim4-parent-arg.mjs <WORK>/consumer-npm
|
|
75
|
+
import.meta.resolve(..., parent=consumer) -> file://<WORK>/consumer-npm/node_modules/research-paper-pipeline/package.json
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**A flag that turns a wrong answer into a right one, rather than an error into an answer, is
|
|
79
|
+
exactly the shape this repository keeps writing tests against.** `createRequire` takes its anchor
|
|
80
|
+
as an argument and has no such mode.
|
|
81
|
+
|
|
82
|
+
### The same probe, proving the anchor is the FILE and not the process
|
|
83
|
+
|
|
84
|
+
`claim4-resolve-apis.mjs` is kept as it was run: it must be **copied into the consumer** and run
|
|
85
|
+
there. Run from the repository instead, every name-based row fails — which is the point:
|
|
86
|
+
|
|
87
|
+
```console
|
|
88
|
+
$ (cd <WORK>/consumer-npm && node claim4-resolve-apis.mjs) # copied in — as run
|
|
89
|
+
OK createRequire.resolve('<pkg>/package.json')
|
|
90
|
+
-> <WORK>/consumer-npm/node_modules/research-paper-pipeline/package.json
|
|
91
|
+
|
|
92
|
+
$ node docs/prior-art/repro/claim4-resolve-apis.mjs # run from the repo
|
|
93
|
+
FAIL createRequire.resolve('<pkg>/package.json')
|
|
94
|
+
MODULE_NOT_FOUND Cannot find module 'research-paper-pipeline/package.json'
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 2. Why `<pkg>/package.json` and not the bare name — and it is NOT the usual reason
|
|
98
|
+
|
|
99
|
+
The textbook trap is that `exports` blocks `./package.json`. **For this package it falls the
|
|
100
|
+
other way**, and the bare name is the spelling that fails:
|
|
101
|
+
|
|
102
|
+
```console
|
|
103
|
+
OK createRequire.resolve('<pkg>/package.json')
|
|
104
|
+
-> …/node_modules/research-paper-pipeline/package.json
|
|
105
|
+
FAIL createRequire.resolve('<pkg>')
|
|
106
|
+
MODULE_NOT_FOUND Cannot find module 'research-paper-pipeline'
|
|
107
|
+
FAIL import.meta.resolve('<pkg>')
|
|
108
|
+
ERR_MODULE_NOT_FOUND Cannot find package '…/node_modules/research-paper-pipeline/index.js'
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The cause is in our own manifest, printed by `claim4-package-dir.mjs`:
|
|
112
|
+
|
|
113
|
+
```console
|
|
114
|
+
manifest.main : undefined
|
|
115
|
+
manifest.exports : undefined
|
|
116
|
+
manifest.bin : {"research-paper-pipeline":"bin/rpp.mjs","rpp":"bin/rpp.mjs"}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
No `main`, no `exports`, so the bare specifier falls through to `index.js`, which does not exist.
|
|
120
|
+
`./package.json` is not blocked because there is no map to block it.
|
|
121
|
+
|
|
122
|
+
**The textbook trap is real, though — it is our own dependency.** In the same run:
|
|
123
|
+
|
|
124
|
+
```console
|
|
125
|
+
FAIL createRequire.resolve('vigiles/package.json')
|
|
126
|
+
ERR_PACKAGE_PATH_NOT_EXPORTED Package subpath './package.json' is not defined by "exports"
|
|
127
|
+
OK createRequire.resolve('vigiles')
|
|
128
|
+
-> …/node_modules/vigiles/dist/test.js
|
|
129
|
+
FAIL createRequire.resolve('vigiles/dist/cli.js')
|
|
130
|
+
ERR_PACKAGE_PATH_NOT_EXPORTED Package subpath './dist/cli.js' is not defined by "exports"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
which is precisely the reason `src/cli.ts` (`runHook`, the comment above the injected `resolve`)
|
|
134
|
+
resolves `"vigiles"` and puts `cli.js` beside the result rather than naming the file. That note is
|
|
135
|
+
correct and this run confirms it.
|
|
136
|
+
|
|
137
|
+
🔴 **`module.findPackageJSON` is the API that does not have this problem.** Same tree, same
|
|
138
|
+
moment, `claim4-find-package-json.mjs`:
|
|
139
|
+
|
|
140
|
+
```console
|
|
141
|
+
findPackageJSON('research-paper-pipeline', consumer/x.js) -> …/node_modules/research-paper-pipeline/package.json
|
|
142
|
+
findPackageJSON('vigiles', consumer/x.js) -> …/node_modules/vigiles/package.json
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
It returned `vigiles/package.json` where `require.resolve` threw. The Node docs do not state that
|
|
146
|
+
it bypasses `exports`; this run does. ⚠️ It is Stability 1.1 and Node ≥22.14 only, so it is
|
|
147
|
+
recorded as the right long-term answer and **not** recommended for a gate that must run on the
|
|
148
|
+
`>=22.13` this package declares in `engines`.
|
|
149
|
+
|
|
150
|
+
## 3. What pnpm and npm actually return, and why it does not matter here
|
|
151
|
+
|
|
152
|
+
```console
|
|
153
|
+
# npm
|
|
154
|
+
=> package dir : <WORK>/consumer-npm/node_modules/research-paper-pipeline
|
|
155
|
+
isSymbolicLink? : false
|
|
156
|
+
SAME as resolved? : true
|
|
157
|
+
|
|
158
|
+
# pnpm
|
|
159
|
+
=> package dir : <WORK>/consumer-pnpm/node_modules/.pnpm/research-paper-pipeline@file+…/node_modules/research-paper-pipeline
|
|
160
|
+
literal node_modules/ : <WORK>/consumer-pnpm/node_modules/research-paper-pipeline
|
|
161
|
+
isSymbolicLink? : true -> .pnpm/research-paper-pipeline@file+…/node_modules/research-paper-pipeline
|
|
162
|
+
SAME as resolved? : true
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Under pnpm the hardcode addresses the **symlink** and resolution addresses the **store realpath**
|
|
166
|
+
(Node realpaths by default; `--preserve-symlinks` flips it, and `resolve-package-path` carries
|
|
167
|
+
`lib/should-preserve-symlinks.js` for exactly that). `realpathSync` of one equals the other, so
|
|
168
|
+
every `existsSync` / `readdirSync` / `readFileSync` in `test/e2e/install.mjs` gives the same answer
|
|
169
|
+
either way. Two interesting asymmetries, both measured:
|
|
170
|
+
|
|
171
|
+
- `module.findPackageJSON` returns the **symlink** path under pnpm, not the store path — the
|
|
172
|
+
opposite of `createRequire`.
|
|
173
|
+
- `findPackageJSON('vigiles', …)` **fails** under pnpm from the consumer root, because pnpm does
|
|
174
|
+
not hoist transitive dependencies. That is the same fact that broke the hook wiring and is
|
|
175
|
+
recorded in `src/cli.ts`; this is an independent reproduction of it.
|
|
176
|
+
|
|
177
|
+
## 4. The mutation: what resolution catches that `existsSync` cannot
|
|
178
|
+
|
|
179
|
+
A directory existing and a package being **reachable by name** are different claims. Add a closed
|
|
180
|
+
`exports` map to the installed manifest and the two diverge:
|
|
181
|
+
|
|
182
|
+
```console
|
|
183
|
+
$ node docs/prior-art/repro/claim5-exports-mutation.mjs <WORK>/consumer-npm
|
|
184
|
+
MUTATION applied: exports = {".":"./bin/rpp.mjs"} (no ./package.json, no ./bin/rpp.mjs)
|
|
185
|
+
1) the HARDCODE still sees everything:
|
|
186
|
+
dir exists : true
|
|
187
|
+
plugin/hooks/h.json : true
|
|
188
|
+
skills/ : true
|
|
189
|
+
2) the documented public import is BROKEN:
|
|
190
|
+
import FAILS: ERR_PACKAGE_PATH_NOT_EXPORTED Package subpath './bin/rpp.mjs' is not defined by "exports"
|
|
191
|
+
3) resolution-based location reports it:
|
|
192
|
+
resolve FAILS: ERR_PACKAGE_PATH_NOT_EXPORTED Package subpath './package.json' is not defined by "exports"
|
|
193
|
+
4) node:module.findPackageJSON, same mutation:
|
|
194
|
+
findPackageJSON -> /tmp/rpp-mutation-X4jYrD/node_modules/research-paper-pipeline/package.json
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`bin/rpp.mjs` documents `import { buildConfig } from "research-paper-pipeline/bin/rpp.mjs"` as a
|
|
198
|
+
public contract. Row 1 is the current test: green. Row 2 is the contract: broken. Row 3 is the
|
|
199
|
+
proposed replacement: red, for the right reason.
|
|
200
|
+
|
|
201
|
+
⚠️ **Row 4 is the honest caveat on the recommendation.** `findPackageJSON` is the better locator
|
|
202
|
+
and the _worse_ canary: it answers past a broken map. Use `createRequire` for the gate.
|
|
203
|
+
|
|
204
|
+
**This is the whole case for the change.** Without this mutation the swap is cosmetic, and this
|
|
205
|
+
repository's rule is that a working tool is replaced when it is shown not to work, not when it
|
|
206
|
+
looks inelegant.
|
|
207
|
+
|
|
208
|
+
## 5. The bin — keep launching the shim, and also resolve the real file
|
|
209
|
+
|
|
210
|
+
The recorded reason for not using `node <path>` is exact, and reproduces:
|
|
211
|
+
|
|
212
|
+
```console
|
|
213
|
+
$ node docs/prior-art/repro/claim5-bin-launch.mjs <WORK>/consumer-pnpm
|
|
214
|
+
<.bin shim> --help rc=0 253ms
|
|
215
|
+
node <.bin shim> --help rc=1 46ms …/node_modules/.bin/rpp:2 basedir=$(dirname …)
|
|
216
|
+
node <manifest bin> --help rc=0 244ms
|
|
217
|
+
npx --no-install rpp --help rc=0 524ms
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Under npm the shim is a symlink (`.bin/rpp -> ../research-paper-pipeline/bin/rpp.mjs`); under
|
|
221
|
+
pnpm it is a `#!/bin/sh` wrapper, and `node` chokes on its second line. But the `bin` value in the
|
|
222
|
+
resolved manifest is the **real file under both**, so `node <that>` works everywhere.
|
|
223
|
+
|
|
224
|
+
| way to get the bin | uniform across npm/pnpm? | cost | what it proves |
|
|
225
|
+
| ---------------------------------------------------- | ------------------------------------------------------ | -------------------------------- | ---------------------------------------------- |
|
|
226
|
+
| `join(consumer, "node_modules/.bin", "rpp")` — today | yes (both managers write it) | none | **the manager created the shim** |
|
|
227
|
+
| `join(installed, manifest.bin.rpp)` | yes | one JSON read already being done | the file the manifest promises exists and runs |
|
|
228
|
+
| `npx --no-install <name>` | yes | ~0.5 s, one process | resolution by name through npm |
|
|
229
|
+
| `npm exec` / `pnpm exec` / `yarn bin` | per-manager commands | ~0.3–0.5 s each | nothing extra |
|
|
230
|
+
| `npm bin` | **gone** — `Unknown command: "bin"` on npm 10.9.7 | — | — |
|
|
231
|
+
| `npm ls <name> --parseable` | prints the path but exits `ELSPROBLEMS` on a pnpm tree | one process | npm's view only |
|
|
232
|
+
|
|
233
|
+
So the shim is not a guess to be removed; it is the only row that observes the manager's own
|
|
234
|
+
work. **What the manifest buys is the second row, free**, plus independence from the bin's _name_
|
|
235
|
+
(`rpp` versus `research-paper-pipeline`, and `.cmd` on Windows — a suffix `netlify/cli` has to
|
|
236
|
+
spell out and this script would not).
|
|
237
|
+
|
|
238
|
+
## 6. Named prior art
|
|
239
|
+
|
|
240
|
+
### Does exactly this
|
|
241
|
+
|
|
242
|
+
| project | file | mechanism |
|
|
243
|
+
| ------------------------------ | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
244
|
+
| **Playwright** | `playwright/lib/util.js:73` | `path.dirname(require.resolve("playwright-core/package.json"))` — the literal recommendation, in a shipped product |
|
|
245
|
+
| **ESLint** | `lib/shared/relative-module-resolver.js:24` | `createRequire(relativeToPath).resolve(moduleName)`, documented as _"This must be a file rather than a directory, but the file need not actually exist"_; the caller is `getPlaceholderPath(cwd) => path.join(cwd, "__placeholder__.js")` (`lib/eslint/eslint-helpers.js:648`) |
|
|
246
|
+
| **resolve-pkg 3.0.1** | `index.js:24–51` | the three-tier ladder: `resolve("<pkg>/package.json")`, else `resolve("<pkg>")` and walk **up** until the directory ends with `node_modules/<pkg>` or its manifest `name` matches, else `findUpSync("node_modules/<pkg>/package.json")`. The ladder exists _because_ tier 1 dies on a closed `exports` map |
|
|
247
|
+
| **resolve-from 4.0.0** | `index.js:27` | the same placeholder trick, spelled `noop.js`, via `Module._resolveFilename` |
|
|
248
|
+
| **lint-staged 17.5.1** | `lib/resolveConfig.js:7` | `createRequire(import.meta.url)` with the comment _"require() does not exist for ESM, so we must create it to use require.resolve()"_ |
|
|
249
|
+
| **resolve-package-path 4.0.3** | `lib/index.js:93` | `pnp ? pnp.resolveToUnqualified(target + "/package.json", baseDir) : …` — the only one of these that handles PnP, and it does so by `require("pnpapi")`, which only exists inside a PnP process |
|
|
250
|
+
|
|
251
|
+
### Adjacent — finds _a_ package root, not _that_ package
|
|
252
|
+
|
|
253
|
+
`pkg-dir` (4.2.0 and 8.0.0) and `read-package-up` are both one line of `findUp("package.json")`
|
|
254
|
+
plus `dirname`. They answer "which package am I inside", walking **up** from a directory. The
|
|
255
|
+
question here is "where did package X land", which walks **down** a resolution path. Different
|
|
256
|
+
question; neither is a candidate.
|
|
257
|
+
|
|
258
|
+
### Solves it by not asking — worth more than the rest
|
|
259
|
+
|
|
260
|
+
**husky 9.1.7**, `index.js:21`:
|
|
261
|
+
|
|
262
|
+
```js
|
|
263
|
+
f.copyFileSync(new URL("husky", import.meta.url), _("h"));
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
husky's git hooks never address `node_modules/husky/…`. On install it **copies its runtime out of
|
|
267
|
+
the package** into `.husky/_/h` and points `core.hooksPath` there, so the hook resolves nothing at
|
|
268
|
+
run time. That is the structural answer to the same problem `plugin/hooks/hooks.json` has, and it
|
|
269
|
+
is the reason husky works under PnP while our hook wiring does not.
|
|
270
|
+
|
|
271
|
+
### Self-location from INSIDE is not the hard case
|
|
272
|
+
|
|
273
|
+
`new URL("…", import.meta.url)` — husky above, `lint-staged/lib/version.js:4`, and our own
|
|
274
|
+
`bin/rpp.mjs:27` (`new URL("../dist/cli.js", import.meta.url)`). No resolution needed. The hard
|
|
275
|
+
case is only the one this script has: locating a package from a **third-party observer process**.
|
|
276
|
+
|
|
277
|
+
## 7. What other packages' own install e2e tests do — they hardcode
|
|
278
|
+
|
|
279
|
+
This is the closest analogue and the answer is plain: **they all hardcode**, and none of them
|
|
280
|
+
resolve.
|
|
281
|
+
|
|
282
|
+
- **netlify/cli**, `e2e/install.e2e.ts:256` — publishes to a local registry, installs under npm,
|
|
283
|
+
pnpm and yarn (classic), then:
|
|
284
|
+
```ts
|
|
285
|
+
const binary = path.resolve(
|
|
286
|
+
path.join(
|
|
287
|
+
cwd,
|
|
288
|
+
`./node_modules/.bin/netlify${platform() === "win32" ? ".cmd" : ""}`,
|
|
289
|
+
),
|
|
290
|
+
);
|
|
291
|
+
```
|
|
292
|
+
Same three-manager matrix as `WANTED`, same `.bin` hardcode, plus the Windows suffix.
|
|
293
|
+
<https://github.com/netlify/cli/blob/main/e2e/install.e2e.ts>
|
|
294
|
+
- **nestjs/terminus**, `tools/pack-check.mjs:32` — `npm pack` → install into `mkdtempSync` →
|
|
295
|
+
```js
|
|
296
|
+
const installed = join(dir, "node_modules", "@nestjs", "terminus");
|
|
297
|
+
```
|
|
298
|
+
and it verifies the public surface by running `node --input-type=module -e "import …"` **with
|
|
299
|
+
`cwd` set to the consumer**, letting Node resolve. That is the resolution check recommended in
|
|
300
|
+
§ 4, arrived at from the other direction.
|
|
301
|
+
<https://github.com/nestjs/terminus/blob/master/tools/pack-check.mjs>
|
|
302
|
+
- **npm/cli** smoke tests, `smoke-tests/test/fixtures/setup.js:22` —
|
|
303
|
+
`const GLOBAL_NODE_MODULES = join(WINDOWS ? '' : 'lib', 'node_modules')`.
|
|
304
|
+
<https://github.com/npm/cli/blob/latest/smoke-tests/test/fixtures/setup.js>
|
|
305
|
+
- A GitHub code search for `"npm pack" "node_modules" "tmpdir" extension:mjs` returns **4 552**
|
|
306
|
+
files; every one sampled builds the path with `join(tmp, "node_modules", …)`.
|
|
307
|
+
|
|
308
|
+
**So the hardcode is the industry norm and is not, by itself, a defect.** The case for changing it
|
|
309
|
+
rests entirely on § 4, not on this section.
|
|
310
|
+
|
|
311
|
+
## 8. Correcting the PnP note in `test-tooling.md`
|
|
312
|
+
|
|
313
|
+
[`test-tooling.md`](test-tooling.md) § "One known limit" says:
|
|
314
|
+
|
|
315
|
+
> **Yarn Berry (PnP) cannot be supported as things stand.** `plugin/hooks/hooks.json` addresses
|
|
316
|
+
> `${CLAUDE_PROJECT_DIR}/node_modules/research-paper-pipeline/bin/rpp.mjs` literally, and PnP has
|
|
317
|
+
> no `node_modules` directory at all. Adding a third `managers()` row for it would be a red test,
|
|
318
|
+
> not a feature.
|
|
319
|
+
|
|
320
|
+
**The conclusion holds. The reason given is incomplete in three ways**, all measured by
|
|
321
|
+
`claim4-yarn-pnp.mjs`:
|
|
322
|
+
|
|
323
|
+
```console
|
|
324
|
+
$ node docs/prior-art/repro/claim4-yarn-pnp.mjs
|
|
325
|
+
yarn add: ok
|
|
326
|
+
node_modules present? NO
|
|
327
|
+
1) plain node, inside the PnP project (no PnP runtime):
|
|
328
|
+
resolve FAILS: MODULE_NOT_FOUND Cannot find module 'research-paper-pipeline/package.json'
|
|
329
|
+
2) yarn node (PnP runtime loaded):
|
|
330
|
+
resolve(pkg/package.json) -> …/.yarn/__virtual__/…/.yarn/berry/cache/research-paper-pipeline-file-919de88475-10c0.zip/node_modules/research-paper-pipeline/package.json
|
|
331
|
+
3) can the OUTSIDE process read the path PnP handed back?
|
|
332
|
+
plain node existsSync: false
|
|
333
|
+
yarn node existsSync: true
|
|
334
|
+
4) `yarn bin rpp`:
|
|
335
|
+
…/cache/research-paper-pipeline-file-919de88475-10c0.zip/node_modules/research-paper-pipeline/bin/rpp.mjs
|
|
336
|
+
5) does the CLI itself run under PnP?
|
|
337
|
+
research-paper-pipeline — machine-checkable gates for a paper kept in git
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
1. 🔴 **The package is not PnP-incompatible — line 5 runs the CLI under PnP and it answers.** The
|
|
341
|
+
note reads as if the package could not work there; what cannot work is one wiring file.
|
|
342
|
+
2. 🔴 **The blocker the note names is the MOVABLE one.** Claude Code exposes
|
|
343
|
+
`${CLAUDE_PLUGIN_ROOT}` — _"Absolute path to the plugin's installation directory … Use it for
|
|
344
|
+
scripts, binaries, and config files bundled with the plugin"_, exported to hook processes
|
|
345
|
+
(<https://code.claude.com/docs/en/plugins-reference>). Rewriting the hook command with it
|
|
346
|
+
removes the literal `node_modules` from `hooks.json`. Whether that is _desirable_ is a separate
|
|
347
|
+
question — the variable is only set when the package is loaded through the plugin channel, and
|
|
348
|
+
today the npm channel is the one that delivers the runnable code — but it is not impossible.
|
|
349
|
+
3. 🔴 **The blocker the note does NOT name is the immovable one, and no resolution API fixes it.**
|
|
350
|
+
PnP resolves to a path **inside a zip**. Line 3: the outside process gets `existsSync: false`
|
|
351
|
+
for the very path PnP just handed it; only PnP's patched `fs` can open it. `test/e2e/install.mjs`
|
|
352
|
+
inspects the installed tree with `existsSync`, `readdirSync` and `readFileSync` **from its own
|
|
353
|
+
process**. Under PnP those read nothing, whatever API produced the path — and `yarn bin rpp`
|
|
354
|
+
(line 4) hands back the same unreadable path, so the supported bin lookup does not help either.
|
|
355
|
+
|
|
356
|
+
**Supporting PnP therefore means re-executing the inspection half of the e2e under `yarn node`**,
|
|
357
|
+
not swapping a path for a resolve call. That is a real piece of work with a real payoff question,
|
|
358
|
+
and it should be recorded as such rather than as "PnP has no node_modules".
|
|
359
|
+
|
|
360
|
+
## 9. The second guess: the base directory for a path written in prose
|
|
361
|
+
|
|
362
|
+
`contentDelivery()` (in `test/e2e/install.mjs`) tries three bases per reference.
|
|
363
|
+
Measured over the real corpus with `claim6-doc-path-candidates.mjs`:
|
|
364
|
+
|
|
365
|
+
```console
|
|
366
|
+
skills : 24
|
|
367
|
+
distinct raw spellings: .claude/skills/paper-pipeline/scripts/announce.mjs
|
|
368
|
+
…
|
|
369
|
+
scripts/pipeline-check.mjs
|
|
370
|
+
../verify-citations/scripts/verify-cites.mjs
|
|
371
|
+
which candidate resolved: {"c1":0,"c2":15,"c3":89,"none":0}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
and `claim6-candidate-ambiguity.mjs`:
|
|
375
|
+
|
|
376
|
+
```console
|
|
377
|
+
total refs 104 ambiguous 0 {"c3":89,"c2":15}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
- **Candidate 1 (`join(installed, raw)`) is dead: 0 of 104.** No skill writes a package-root
|
|
381
|
+
relative path, and none has since the corpus existed.
|
|
382
|
+
- **Candidates 2 and 3 are disjoint by spelling**, zero references resolve under both. So the
|
|
383
|
+
`some()` is not currently masking a wrong-base reference — an honest negative.
|
|
384
|
+
- The corpus carries **two conventions at once**: project-root-relative
|
|
385
|
+
(`.claude/skills/paper-pipeline/scripts/x.mjs`, 89 refs) and skill-relative (`scripts/x.mjs`
|
|
386
|
+
and `../other-skill/scripts/x.mjs`, 15 refs).
|
|
387
|
+
|
|
388
|
+
### Prior art for validating file references inside markdown
|
|
389
|
+
|
|
390
|
+
There is essentially one pattern, and it is not a ladder of candidates.
|
|
391
|
+
|
|
392
|
+
- **remark-validate-links 13.1.0** resolves every link against the **file's own directory** —
|
|
393
|
+
`lib/index.js:282`, `base: absolute ? path.dirname(absolute) : file.cwd` — and takes the one
|
|
394
|
+
thing it cannot derive as an explicit option: `root` _("path to Git root folder; if both `root`
|
|
395
|
+
and `repository` are nullish, the Git root is detected")_. Detection is a single documented
|
|
396
|
+
fallback with a stated failure mode, not a list of guesses.
|
|
397
|
+
<https://github.com/remarkjs/remark-validate-links>
|
|
398
|
+
- **Claude Code plugins** declare it: _"All paths must be relative to the plugin root and start
|
|
399
|
+
with `./`"_, plus `${CLAUDE_PLUGIN_ROOT}` for scripts.
|
|
400
|
+
<https://code.claude.com/docs/en/plugins-reference>
|
|
401
|
+
- **This package already declares it once** — `.claude-plugin/plugin.json`:
|
|
402
|
+
`"skills": "./skills/"`.
|
|
403
|
+
|
|
404
|
+
**What declaring it would look like here:** one convention for the prose — skill-relative, since
|
|
405
|
+
that is what survives both channels — and the test resolving against exactly one base, with
|
|
406
|
+
candidate 1 deleted and candidate 3 kept only as an explicitly named legacy spelling with its own
|
|
407
|
+
counter, so that "89 references still use the retired spelling" is a number the run prints rather
|
|
408
|
+
than a fact it hides behind an `||`. That is the same shape as the `managers()` rewrite: a
|
|
409
|
+
declared skip beats a silent one.
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## Status
|
|
414
|
+
|
|
415
|
+
Written 2026-09-19. Every table cell above is either a pasted run from [`repro/`](repro/README.md)
|
|
416
|
+
or a quotation from the URL named beside it. Nothing here is recalled.
|
|
417
|
+
|
|
418
|
+
**What is NOT measured, and so not claimed:** behaviour on Windows (the `.cmd` shim, the pnpm
|
|
419
|
+
`.ps1`/`.cmd` wrappers); npm workspaces, where a hoisted install would put the package at the
|
|
420
|
+
workspace root rather than in the consumer — the e2e builds a standalone temp project, so this is
|
|
421
|
+
untested and merely plausible; yarn _classic_, which has a `node_modules` layout and is one
|
|
422
|
+
`managers()` row away but was not run.
|