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,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The three editor hooks, wired into `<project>/.claude/settings.json` โ the file Claude Code
|
|
3
|
+
* documents as the way to share hooks with a team ("Commit `.claude/settings.json` so everyone
|
|
4
|
+
* who clones the repository gets the same โฆ hooks"). `paperlint init` writes it; `paperlint doctor` reads it.
|
|
5
|
+
*
|
|
6
|
+
* ๐ด WHY THIS REPLACED THE PLUGIN AS THE CARRIER (docs/prior-art/paper-folder-scaffolding.md ยง 5).
|
|
7
|
+
* The plugin needed two `/plugin` lines typed by a human inside Claude Code: an agent installing
|
|
8
|
+
* this package cannot type them, `paperlint doctor` cannot see their effect, and a plugin that the
|
|
9
|
+
* REPOSITORY declares (`enabledPlugins`) is not installed in a cloud session. A settings file is
|
|
10
|
+
* an ordinary file edit, it is committed, and a single-repository cloud session reads its hooks.
|
|
11
|
+
* (A plugin the USER installed at account level does load in the cloud โ but that is per person,
|
|
12
|
+
* so a collaborator or a fresh account gets nothing, and doctor still cannot see it.)
|
|
13
|
+
*
|
|
14
|
+
* โโ THE ONE SOURCE โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
15
|
+
* The wiring is read from `plugin/hooks/hooks.json`, never copied. The hook NAMES are derived
|
|
16
|
+
* from its commands, so a hook added there is wired and checked here without a second list.
|
|
17
|
+
*
|
|
18
|
+
* โโ THE MERGE IS vigiles'S, NOT OURS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
19
|
+
* `claudeCodeHookProtocol.mergeRegistrations(existing, compiled, managedBy)` from the public
|
|
20
|
+
* `vigiles/claude-code` entry. Ownership is decided per COMMAND by the path token, so a user's
|
|
21
|
+
* own hook sharing a matcher block with ours survives, and a second run changes nothing. It is
|
|
22
|
+
* passed in rather than imported here: loading `vigiles/claude-code` costs ~140 ms and pulls
|
|
23
|
+
* `@ast-grep/napi`, which `paperlint lint` must not pay for. Only `init` loads it.
|
|
24
|
+
*/
|
|
25
|
+
// eslint-disable-next-line boundaries/dependencies -- legacy I/O, moves behind a port in #76
|
|
26
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
27
|
+
import { basename, dirname, join, posix } from "node:path";
|
|
28
|
+
import { fileURLToPath } from "node:url";
|
|
29
|
+
import {
|
|
30
|
+
LEGACY_PACKAGE_NAME,
|
|
31
|
+
PACKAGE_NAME,
|
|
32
|
+
} from "../skills/paper-pipeline/scripts/consumer.mjs";
|
|
33
|
+
|
|
34
|
+
/** The committed, shared settings file โ husky's `.husky/` analogue, except git carries it. */
|
|
35
|
+
export const SETTINGS_PATH = join(".claude", "settings.json");
|
|
36
|
+
|
|
37
|
+
/** Where the wiring lives in this package. The same file the plugin serves, read, not copied. */
|
|
38
|
+
export const WIRING_FILE = fileURLToPath(
|
|
39
|
+
new URL("../plugin/hooks/hooks.json", import.meta.url),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The path token vigiles' merge keys ownership on. Relative on purpose: the merge strips quotes
|
|
44
|
+
* and `${CLAUDE_PROJECT_DIR}/` before comparing, so this is the same file as the spelling in
|
|
45
|
+
* `hooks.json`, and nothing machine-specific lands in a committed file.
|
|
46
|
+
*/
|
|
47
|
+
export const MANAGED_BY = `node_modules/${PACKAGE_NAME}/bin/rpp.mjs`;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The same token as an install under the package's old name wrote it. A command spelled this way
|
|
51
|
+
* is ours from before the rename: `init` removes it and writes the current one, `doctor` names it.
|
|
52
|
+
*/
|
|
53
|
+
export const LEGACY_MANAGED_BY = `node_modules/${LEGACY_PACKAGE_NAME}/bin/rpp.mjs`;
|
|
54
|
+
|
|
55
|
+
/** The names the package's directory under `node_modules` has had. */
|
|
56
|
+
const PKG_DIRS: readonly string[] = [PACKAGE_NAME, LEGACY_PACKAGE_NAME];
|
|
57
|
+
|
|
58
|
+
export interface HookCommand {
|
|
59
|
+
readonly type: string;
|
|
60
|
+
readonly command: string;
|
|
61
|
+
}
|
|
62
|
+
export interface HookEntry {
|
|
63
|
+
readonly matcher?: string;
|
|
64
|
+
readonly hooks: readonly HookCommand[];
|
|
65
|
+
}
|
|
66
|
+
export type HooksMap = Readonly<Record<string, readonly HookEntry[]>>;
|
|
67
|
+
|
|
68
|
+
/** A settings object as far as this module cares: anything, plus maybe `hooks` and plugins. */
|
|
69
|
+
export type Settings = Record<string, unknown>;
|
|
70
|
+
|
|
71
|
+
/** vigiles' merge, as the one function this module needs from it. */
|
|
72
|
+
export type Merge = (
|
|
73
|
+
existing: Settings,
|
|
74
|
+
compiled: HooksMap,
|
|
75
|
+
managedBy: string,
|
|
76
|
+
) => Settings;
|
|
77
|
+
|
|
78
|
+
/** One shell token reduced to the path it names โ the same two strips vigiles' merge applies. */
|
|
79
|
+
function bare(token: string): string {
|
|
80
|
+
const unquoted = token.replace(/^["']/, "").replace(/["']$/, "");
|
|
81
|
+
return unquoted.replace(/^\$\{?CLAUDE_PROJECT_DIR\}?[/\\]/, "");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Which paperlint hook a command runs, if any, and whether it is spelled the way `init` writes it.
|
|
86
|
+
*
|
|
87
|
+
* Recognised spellings โ each is ONE lexeme of a shell command, which is what a command is:
|
|
88
|
+
* `node <โฆ>/node_modules/paperlint/bin/rpp.mjs hook <name>` ours when the path is exactly
|
|
89
|
+
* MANAGED_BY
|
|
90
|
+
* the same under `node_modules/research-paper-pipeline/` `legacy`: ours before the rename
|
|
91
|
+
* `npx paperlint hook <name>`, `node_modules/.bin/paperlint hook <name>` (or the old bin names)
|
|
92
|
+
* another spelling
|
|
93
|
+
* `vigiles โฆ run-program <โฆ>/node_modules/<pkg>/hooks/<name>.hook.mjs`
|
|
94
|
+
* another spelling
|
|
95
|
+
* The last is how the one real consumer wired all three by hand before `init` could.
|
|
96
|
+
*/
|
|
97
|
+
export function hookRun(command: string): {
|
|
98
|
+
readonly name: string;
|
|
99
|
+
readonly ours: boolean;
|
|
100
|
+
readonly legacy: boolean;
|
|
101
|
+
} | null {
|
|
102
|
+
const tokens = command.trim().split(/\s+/).map(bare);
|
|
103
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
104
|
+
const t = posix.normalize(tokens[i] ?? "");
|
|
105
|
+
const parts = t.split("/");
|
|
106
|
+
const at = parts.findIndex(
|
|
107
|
+
(p, j) => p === "node_modules" && PKG_DIRS.includes(parts[j + 1] ?? ""),
|
|
108
|
+
);
|
|
109
|
+
const inside = at === -1 ? [] : parts.slice(at + 2);
|
|
110
|
+
const isBin =
|
|
111
|
+
(inside[0] === "bin" && inside[1] === "rpp.mjs") ||
|
|
112
|
+
["rpp", ...PKG_DIRS].includes(basename(t));
|
|
113
|
+
const name = tokens[i + 2];
|
|
114
|
+
if (isBin && tokens[i + 1] === "hook" && name)
|
|
115
|
+
return {
|
|
116
|
+
name,
|
|
117
|
+
ours: t === MANAGED_BY,
|
|
118
|
+
legacy: t === LEGACY_MANAGED_BY,
|
|
119
|
+
};
|
|
120
|
+
if (inside[0] === "hooks" && inside[1]?.endsWith(".hook.mjs"))
|
|
121
|
+
return {
|
|
122
|
+
name: basename(inside[1], ".hook.mjs"),
|
|
123
|
+
ours: false,
|
|
124
|
+
legacy: false,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Every command in a settings object, with the event it hangs off. */
|
|
131
|
+
export function commandsIn(
|
|
132
|
+
settings: Settings,
|
|
133
|
+
): { readonly event: string; readonly command: string }[] {
|
|
134
|
+
const hooks = settings["hooks"];
|
|
135
|
+
if (!hooks || typeof hooks !== "object") return [];
|
|
136
|
+
const out: { event: string; command: string }[] = [];
|
|
137
|
+
for (const [event, entries] of Object.entries(hooks as HooksMap))
|
|
138
|
+
for (const entry of Array.isArray(entries) ? entries : [])
|
|
139
|
+
for (const h of Array.isArray(entry?.hooks) ? entry.hooks : [])
|
|
140
|
+
if (typeof h?.command === "string")
|
|
141
|
+
out.push({ event, command: h.command });
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface Wiring {
|
|
146
|
+
readonly compiled: HooksMap;
|
|
147
|
+
/** Derived from the commands, in order. Never empty โ an empty set throws. */
|
|
148
|
+
readonly names: readonly string[];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The shipped wiring, and the hook names it runs. An unreadable file or zero names THROWS: a
|
|
153
|
+
* check that silently wired nothing would report "โ wired" over an empty set.
|
|
154
|
+
*/
|
|
155
|
+
export function shippedWiring(file: string = WIRING_FILE): Wiring {
|
|
156
|
+
const json = JSON.parse(readFileSync(file, "utf8")) as { hooks?: HooksMap };
|
|
157
|
+
const compiled = json.hooks ?? {};
|
|
158
|
+
const names = commandsIn({ hooks: compiled })
|
|
159
|
+
.map((c) => hookRun(c.command)?.name)
|
|
160
|
+
.filter((n): n is string => typeof n === "string");
|
|
161
|
+
if (names.length === 0)
|
|
162
|
+
throw new Error(
|
|
163
|
+
`${file} names no paperlint hook โ there is nothing to wire`,
|
|
164
|
+
);
|
|
165
|
+
return { compiled, names: [...new Set(names)] };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** How many commands run each shipped hook, split by spelling. */
|
|
169
|
+
export function wiredCounts(
|
|
170
|
+
settings: Settings,
|
|
171
|
+
names: readonly string[],
|
|
172
|
+
): Map<string, { ours: number; legacy: number; other: number }> {
|
|
173
|
+
const counts = new Map(
|
|
174
|
+
names.map((n) => [n, { ours: 0, legacy: 0, other: 0 }]),
|
|
175
|
+
);
|
|
176
|
+
for (const { command } of commandsIn(settings)) {
|
|
177
|
+
const run = hookRun(command);
|
|
178
|
+
const slot = run ? counts.get(run.name) : undefined;
|
|
179
|
+
if (run && slot)
|
|
180
|
+
if (run.ours) slot.ours++;
|
|
181
|
+
else if (run.legacy) slot.legacy++;
|
|
182
|
+
else slot.other++;
|
|
183
|
+
}
|
|
184
|
+
return counts;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* The settings with every command an install under the old package name wrote removed, and the
|
|
189
|
+
* matcher blocks and events left empty by that removal dropped. The user's own commands stay.
|
|
190
|
+
*/
|
|
191
|
+
export function withoutLegacy(settings: Settings): {
|
|
192
|
+
readonly settings: Settings;
|
|
193
|
+
readonly removed: number;
|
|
194
|
+
} {
|
|
195
|
+
const hooks = settings["hooks"];
|
|
196
|
+
if (!hooks || typeof hooks !== "object") return { settings, removed: 0 };
|
|
197
|
+
let removed = 0;
|
|
198
|
+
const next: Record<string, HookEntry[]> = {};
|
|
199
|
+
for (const [event, entries] of Object.entries(hooks as HooksMap)) {
|
|
200
|
+
const kept: HookEntry[] = [];
|
|
201
|
+
for (const entry of Array.isArray(entries) ? entries : []) {
|
|
202
|
+
const list: readonly HookCommand[] = Array.isArray(entry?.hooks)
|
|
203
|
+
? entry.hooks
|
|
204
|
+
: [];
|
|
205
|
+
const own = list.filter((h) => {
|
|
206
|
+
const legacy =
|
|
207
|
+
typeof h?.command === "string" && hookRun(h.command)?.legacy === true;
|
|
208
|
+
if (legacy) removed++;
|
|
209
|
+
return !legacy;
|
|
210
|
+
});
|
|
211
|
+
if (own.length > 0 || list.length === 0)
|
|
212
|
+
kept.push({ ...entry, hooks: own });
|
|
213
|
+
}
|
|
214
|
+
if (kept.length > 0) next[event] = kept;
|
|
215
|
+
}
|
|
216
|
+
return removed === 0
|
|
217
|
+
? { settings, removed }
|
|
218
|
+
: { settings: { ...settings, hooks: next }, removed };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Plugin ids the PROJECT enables for this package. A user-scope plugin lives in the user's own
|
|
223
|
+
* settings, which this cannot see โ callers must say so rather than print "not enabled".
|
|
224
|
+
*/
|
|
225
|
+
export function pluginEnabledHere(settings: Settings): string[] {
|
|
226
|
+
const enabled = settings["enabledPlugins"];
|
|
227
|
+
if (!enabled || typeof enabled !== "object") return [];
|
|
228
|
+
return Object.entries(enabled as Record<string, unknown>)
|
|
229
|
+
.filter(
|
|
230
|
+
([id, on]) => on === true && id.split("@")[0] === LEGACY_PACKAGE_NAME,
|
|
231
|
+
)
|
|
232
|
+
.map(([id]) => id);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export type SettingsRead =
|
|
236
|
+
| { readonly status: "absent"; readonly path: string; readonly settings: {} }
|
|
237
|
+
| {
|
|
238
|
+
readonly status: "read";
|
|
239
|
+
readonly path: string;
|
|
240
|
+
readonly settings: Settings;
|
|
241
|
+
readonly raw: string;
|
|
242
|
+
}
|
|
243
|
+
| {
|
|
244
|
+
readonly status: "unparsable";
|
|
245
|
+
readonly path: string;
|
|
246
|
+
readonly reason: string;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
export function readSettings(root: string): SettingsRead {
|
|
250
|
+
const path = join(root, SETTINGS_PATH);
|
|
251
|
+
if (!existsSync(path)) return { status: "absent", path, settings: {} };
|
|
252
|
+
const raw = readFileSync(path, "utf8");
|
|
253
|
+
try {
|
|
254
|
+
const parsed: unknown = JSON.parse(raw);
|
|
255
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
256
|
+
return { status: "unparsable", path, reason: "not a JSON object" };
|
|
257
|
+
return { status: "read", path, settings: parsed as Settings, raw };
|
|
258
|
+
} catch (e) {
|
|
259
|
+
return { status: "unparsable", path, reason: (e as Error).message };
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export type WireResult =
|
|
264
|
+
| {
|
|
265
|
+
readonly status: "written" | "present";
|
|
266
|
+
readonly path: string;
|
|
267
|
+
readonly names: readonly string[];
|
|
268
|
+
readonly plugin: readonly string[];
|
|
269
|
+
/** Commands an install under the old package name wrote, removed before the merge. */
|
|
270
|
+
readonly replaced: number;
|
|
271
|
+
}
|
|
272
|
+
| {
|
|
273
|
+
readonly status: "foreign";
|
|
274
|
+
readonly path: string;
|
|
275
|
+
readonly names: readonly string[];
|
|
276
|
+
/** The commands that already run a shipped hook under another spelling. */
|
|
277
|
+
readonly found: readonly {
|
|
278
|
+
readonly name: string;
|
|
279
|
+
readonly command: string;
|
|
280
|
+
}[];
|
|
281
|
+
/** Shipped hooks that no command runs in ANY spelling โ left unwired as well. */
|
|
282
|
+
readonly missing: readonly string[];
|
|
283
|
+
readonly plugin: readonly string[];
|
|
284
|
+
}
|
|
285
|
+
| {
|
|
286
|
+
readonly status: "unparsable";
|
|
287
|
+
readonly path: string;
|
|
288
|
+
readonly reason: string;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Merge the shipped hooks into `.claude/settings.json`. Idempotent: when the merge changes
|
|
293
|
+
* nothing, the file is NOT rewritten, so a second run is byte-identical even when the user's own
|
|
294
|
+
* formatting differs from ours.
|
|
295
|
+
*
|
|
296
|
+
* ๐ด ANOTHER SPELLING OF THE SAME HOOK STOPS THE WRITE. vigiles' merge owns only commands whose
|
|
297
|
+
* path token is MANAGED_BY; a hook wired by hand through `vigiles โฆ run-program โฆ/hooks/x.hook.mjs`
|
|
298
|
+
* is not recognised as ours, so a merge would ADD a second copy โ and Claude Code dedupes only
|
|
299
|
+
* identical handlers, so the guard would run twice. Writing nothing and saying so is the only
|
|
300
|
+
* answer that cannot make it worse.
|
|
301
|
+
*/
|
|
302
|
+
export function wireHooks(
|
|
303
|
+
root: string,
|
|
304
|
+
merge: Merge,
|
|
305
|
+
wiring: Wiring = shippedWiring(),
|
|
306
|
+
): WireResult {
|
|
307
|
+
const read = readSettings(root);
|
|
308
|
+
if (read.status === "unparsable") return read;
|
|
309
|
+
const { path } = read;
|
|
310
|
+
// Commands from an install under the old package name point at a directory that is gone after
|
|
311
|
+
// the upgrade. They are ours, so they are replaced rather than counted as someone else's.
|
|
312
|
+
const { settings, removed } = withoutLegacy(read.settings);
|
|
313
|
+
const plugin = pluginEnabledHere(settings);
|
|
314
|
+
const found = commandsIn(settings)
|
|
315
|
+
.map(({ command }) => ({ command, run: hookRun(command) }))
|
|
316
|
+
.filter(({ run }) => run && !run.ours && wiring.names.includes(run.name))
|
|
317
|
+
.map(({ command, run }) => ({ name: run?.name ?? "", command }));
|
|
318
|
+
if (found.length > 0) {
|
|
319
|
+
const counts = wiredCounts(settings, wiring.names);
|
|
320
|
+
const missing = wiring.names.filter(
|
|
321
|
+
(n) => (counts.get(n)?.ours ?? 0) + (counts.get(n)?.other ?? 0) === 0,
|
|
322
|
+
);
|
|
323
|
+
return {
|
|
324
|
+
status: "foreign",
|
|
325
|
+
path,
|
|
326
|
+
names: wiring.names,
|
|
327
|
+
found,
|
|
328
|
+
missing,
|
|
329
|
+
plugin,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const next = merge(settings, wiring.compiled, MANAGED_BY);
|
|
334
|
+
if (JSON.stringify(next) === JSON.stringify(read.settings))
|
|
335
|
+
return {
|
|
336
|
+
status: "present",
|
|
337
|
+
path,
|
|
338
|
+
names: wiring.names,
|
|
339
|
+
plugin,
|
|
340
|
+
replaced: 0,
|
|
341
|
+
};
|
|
342
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
343
|
+
writeFileSync(path, JSON.stringify(next, null, 2) + "\n", "utf8");
|
|
344
|
+
return {
|
|
345
|
+
status: "written",
|
|
346
|
+
path,
|
|
347
|
+
names: wiring.names,
|
|
348
|
+
plugin,
|
|
349
|
+
replaced: removed,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** The consequence every report of a written hook must carry, in one place. */
|
|
354
|
+
export const FRESH_CLONE_NOTE = `the commands point into node_modules/${PACKAGE_NAME}/ โ in a fresh clone they cannot run until \`npm install\` has run there`;
|
|
355
|
+
|
|
356
|
+
/** The instruction for a project that also enables the plugin (it only ever existed under the old name). */
|
|
357
|
+
export const UNINSTALL_PLUGIN = `/plugin uninstall ${LEGACY_PACKAGE_NAME}@${LEGACY_PACKAGE_NAME}`;
|
|
358
|
+
|
|
359
|
+
/** The doctor lines for hook commands an install under the old package name left behind. */
|
|
360
|
+
function legacyLines(
|
|
361
|
+
counts: ReadonlyMap<string, { readonly legacy: number }>,
|
|
362
|
+
): string[] {
|
|
363
|
+
let legacy = 0;
|
|
364
|
+
for (const c of counts.values()) legacy += c.legacy;
|
|
365
|
+
return legacy === 0
|
|
366
|
+
? []
|
|
367
|
+
: [
|
|
368
|
+
` โ ${String(legacy)} hook command(s) still point into node_modules/${LEGACY_PACKAGE_NAME}/, the package's old name โ they no longer run.`,
|
|
369
|
+
` \`npx ${PACKAGE_NAME} init\` replaces them`,
|
|
370
|
+
];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* `paperlint doctor`'s section. ADVISORY โ it never fails the run: the hooks are an in-editor guard,
|
|
375
|
+
* `--no-hooks` is a legitimate choice, and a doctor that exits non-zero on a choice gets muted.
|
|
376
|
+
*/
|
|
377
|
+
export function doctorHooks(
|
|
378
|
+
root: string,
|
|
379
|
+
wiring: Wiring = shippedWiring(),
|
|
380
|
+
): string[] {
|
|
381
|
+
const out = [`hooks (Claude Code reads them from ${SETTINGS_PATH})`];
|
|
382
|
+
const read = readSettings(root);
|
|
383
|
+
if (read.status === "unparsable") {
|
|
384
|
+
out.push(` โ ${SETTINGS_PATH} does not parse โ ${read.reason}`);
|
|
385
|
+
out.push(` Claude Code cannot read it either; fix the JSON`);
|
|
386
|
+
return out;
|
|
387
|
+
}
|
|
388
|
+
const counts = wiredCounts(read.settings, wiring.names);
|
|
389
|
+
const total = (n: string): number =>
|
|
390
|
+
(counts.get(n)?.ours ?? 0) + (counts.get(n)?.other ?? 0);
|
|
391
|
+
const missing = wiring.names.filter((n) => total(n) === 0);
|
|
392
|
+
const twice = wiring.names.filter((n) => total(n) > 1);
|
|
393
|
+
// `init` refuses to write while any hook is wired under another spelling, so its remedy must
|
|
394
|
+
// not be offered then โ the remedy is to pick ONE form.
|
|
395
|
+
const handWired = wiring.names.some((n) => (counts.get(n)?.other ?? 0) > 0);
|
|
396
|
+
const remedy = handWired
|
|
397
|
+
? `some paperlint hooks are wired by hand under another spelling, and \`npx paperlint init\` writes nothing then โ add the missing ones in that same form, or delete the hand-written ones and run \`npx paperlint init\``
|
|
398
|
+
: `\`npx paperlint init\` adds them`;
|
|
399
|
+
if (twice.length > 0) {
|
|
400
|
+
out.push(` โ wired TWICE โ each of these runs more than once per event:`);
|
|
401
|
+
for (const n of twice) out.push(` ${n} ร${String(total(n))}`);
|
|
402
|
+
out.push(
|
|
403
|
+
` keep one command per hook; \`npx paperlint init\` writes the rpp.mjs form`,
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
if (missing.length === wiring.names.length)
|
|
407
|
+
out.push(
|
|
408
|
+
` โ not wired โ none of the ${String(wiring.names.length)} hooks is in ${SETTINGS_PATH}. ` +
|
|
409
|
+
`\`npx paperlint init\` wires them`,
|
|
410
|
+
);
|
|
411
|
+
else if (missing.length > 0)
|
|
412
|
+
out.push(
|
|
413
|
+
` โ partly wired โ missing: ${missing.join(", ")}`,
|
|
414
|
+
` ${remedy}`,
|
|
415
|
+
);
|
|
416
|
+
else if (twice.length === 0)
|
|
417
|
+
out.push(
|
|
418
|
+
` โ wired โ ${wiring.names.join(", ")}, once each`,
|
|
419
|
+
` ${FRESH_CLONE_NOTE}`,
|
|
420
|
+
);
|
|
421
|
+
out.push(...legacyLines(counts));
|
|
422
|
+
const plugin = pluginEnabledHere(read.settings);
|
|
423
|
+
if (plugin.length > 0) {
|
|
424
|
+
out.push(
|
|
425
|
+
` โ this project ALSO enables the plugin (${plugin.join(", ")}) โ every hook would run twice.`,
|
|
426
|
+
` the plugin no longer carries the hooks: ${UNINSTALL_PLUGIN}`,
|
|
427
|
+
` and remove it from "enabledPlugins" in ${SETTINGS_PATH}`,
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
out.push(
|
|
431
|
+
` ยท a plugin installed at USER scope is not visible from here โ \`/plugin\` inside Claude Code shows it`,
|
|
432
|
+
);
|
|
433
|
+
return out;
|
|
434
|
+
}
|