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,538 @@
|
|
|
1
|
+
# Paper folders and hooks — who creates them, and what the human still types
|
|
2
|
+
|
|
3
|
+
Written 2026-09-23. Two questions that turned out to be one: **what does a command do, and what is
|
|
4
|
+
still left to a human copying something by hand?**
|
|
5
|
+
|
|
6
|
+
> **Status, 2026-09-23 — implemented, with corrections.** The template moved into files
|
|
7
|
+
> (`templates/paper/`), `rpp new` exists, and `rpp init` writes the hooks into
|
|
8
|
+
> `.claude/settings.json` with the two-mode behaviour of § 6. An independent review returned
|
|
9
|
+
> HOLDS WITH CAVEATS; its corrections are applied below and marked **[corrected]**:
|
|
10
|
+
>
|
|
11
|
+
> - the cloud claim was too strong. Plugins the **user** installed or synced at account level do
|
|
12
|
+
> load in a cloud session; what does not load is a plugin **declared by the repository**
|
|
13
|
+
> (`enabledPlugins` / `extraKnownMarketplaces` in the repo's settings);
|
|
14
|
+
> - `.claude/settings.json` hooks load in the cloud in **single-repo sessions** only;
|
|
15
|
+
> - `rpp freeze` is **not part of this change** — it stays a later step;
|
|
16
|
+
> - `--hooks=local` is **deferred, not implemented**; `init` refuses it by name.
|
|
17
|
+
>
|
|
18
|
+
> Where the build differed from this text, it is listed at the end, in § 8.
|
|
19
|
+
>
|
|
20
|
+
> **Later, 2026-09-24:** Markdown papers are deprecated and being removed
|
|
21
|
+
> ([#57](https://github.com/zernie/research-paper-pipeline/issues/57)). Where this record mentions
|
|
22
|
+
> `paper.md` or `--format md`, it describes the tool as it was when written.
|
|
23
|
+
|
|
24
|
+
1. A paper folder needs a `PIPELINE-STATUS.md`, and today nothing makes it — you copy a template.
|
|
25
|
+
Should there be a command, and where should per-paper settings live: in a file in every paper
|
|
26
|
+
folder, or in one config that lists the folders?
|
|
27
|
+
2. `rpp init` ends by printing two Claude Code commands to type. Without them the three editor
|
|
28
|
+
hooks (including the blocking `paper-edit-guard`) are not active. Should `init` wire the hooks
|
|
29
|
+
itself?
|
|
30
|
+
|
|
31
|
+
## The answer in five lines
|
|
32
|
+
|
|
33
|
+
- **Config topology: keep what exists.** The root config names the papers _directory_ (a scope),
|
|
34
|
+
never a list of papers; each paper carries its own settings in its own folder. Every comparable
|
|
35
|
+
tool does it this way, and the one that keeps an explicit list (Cargo) had to teach `cargo new`
|
|
36
|
+
to edit that list so it does not drift.
|
|
37
|
+
- **New command `rpp new <name>`** scaffolds a paper folder the linter accepts, from a real template
|
|
38
|
+
file (Hugo's "archetype" model). On an existing folder it only adds what is missing (`cargo init`).
|
|
39
|
+
- **Later, not in this change: `rpp freeze <paper> <stage>`** would copy the PDF and source into
|
|
40
|
+
`versions/` and append the `stages` record with the byte counts. Those numbers are tool-written
|
|
41
|
+
state, like a lockfile — no human should type `bytes: 305337`.
|
|
42
|
+
- **Hooks: `rpp init` writes them into `.claude/settings.json`** (idempotent merge, never touching
|
|
43
|
+
the user's own entries), and the plugin stops being the hook carrier. A plugin the repository
|
|
44
|
+
declares is not installed in a cloud session **[corrected: a user-installed plugin is]**, the
|
|
45
|
+
`/plugin` route cannot be run by an agent, and its effect cannot be checked by `rpp doctor`; the
|
|
46
|
+
one real consumer already wires the hooks in `settings.json` by hand.
|
|
47
|
+
- **`init` asks only when a human is at a terminal**, and has a stated default for everything else.
|
|
48
|
+
|
|
49
|
+
The smallest first step: `rpp new`, copying a template file that ships in the package. No rule
|
|
50
|
+
changes, no migration.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 1. The current design, from the code
|
|
55
|
+
|
|
56
|
+
### How rpp finds papers
|
|
57
|
+
|
|
58
|
+
- **One key in the consumer's `package.json`**, `research-paper-pipeline.papers` (renamed `papersDir` on 2026-09-24), default `papers`
|
|
59
|
+
(`lib/paper-config.mjs:24`, `:27`). `rpp.json` is still read as a deprecated fallback
|
|
60
|
+
(`src/cli.ts` `findDeclaration`, and `docs/configuration.md` § "Why the key lives in
|
|
61
|
+
`package.json`").
|
|
62
|
+
- **`rpp init` measures the papers root**: `detectPapers` (`src/doctor.ts:97`) walks two levels
|
|
63
|
+
for a directory whose _children_ hold a paper marker. One hit is used, several are asked about,
|
|
64
|
+
none falls back to `papers` and is labelled a guess (`src/init.ts` `choosePapers`). It then writes
|
|
65
|
+
the key into `package.json` (`declarePapers`), links the skills into `.claude/skills/`
|
|
66
|
+
(`src/init.ts:448`), offers a CI workflow, reports missing programs, and **prints** the two
|
|
67
|
+
`/plugin` lines (`src/init.ts:257-258`). It never creates the papers directory and never creates
|
|
68
|
+
a paper.
|
|
69
|
+
- **A directory is a paper** if it holds any of `PIPELINE-STATUS.md`, `paper.tex`, `paper.md`,
|
|
70
|
+
`venue.json` (`src/build.ts:41-46`, `src/structure.ts:39`). Detection is generous.
|
|
71
|
+
- **`rpp lint` requires `PIPELINE-STATUS.md`** in every such directory, plus one of
|
|
72
|
+
`paper.tex`/`paper.md` (`src/structure.ts:38-43`). A paper with only `paper.md` fails the very
|
|
73
|
+
first `rpp lint` with `missing PIPELINE-STATUS.md` — that is the README's own "First run" example.
|
|
74
|
+
|
|
75
|
+
⚠️ **One inconsistency worth knowing.** `paper/stages` says in its header that a missing `stages`
|
|
76
|
+
field is _legitimate_ — "a paper that has shipped nothing owes nothing" (`eslint-rules/paper-stages.mjs:24-27`) —
|
|
77
|
+
while `structure` makes the _file_ that would carry the field an unconditional error. A brand-new
|
|
78
|
+
draft is therefore red for lacking a file whose only machine-read content would be empty. With
|
|
79
|
+
`rpp new` the requirement stops costing a hand-copy, so this doc does not propose relaxing it; it is
|
|
80
|
+
listed so nobody thinks the two are consistent.
|
|
81
|
+
|
|
82
|
+
### Which rules read which part of `PIPELINE-STATUS.md`
|
|
83
|
+
|
|
84
|
+
| rule / reader | where | reads |
|
|
85
|
+
| ----------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| `paper/stages` (error) | `eslint-rules/paper-stages.mjs:91`, glob `src/cli.ts:126` | front matter `stages[]`: `stage`, `date`, `pdf`, `bytes`; and lists `versions/*.pdf` |
|
|
87
|
+
| `paper/source` (error) | `eslint-rules/paper-stages.mjs:377`, `:415`, `:442` | `stages[].source`, `sourceBytes`, `sourceLost` |
|
|
88
|
+
| `paper/author-list` (warn) | `eslint-rules/paper-stages.mjs:296`, marker `:325` | `stages[]` + searches the scorecard **table cells** for the marker `bib-authors` |
|
|
89
|
+
| `paper/research-question` (warn) | `eslint-rules/paper-research-question.mjs:142` | reads the status file from disk while linting `paper.tex`/`paper.md`: `stages`, `researchQuestion` |
|
|
90
|
+
| `paper/typography` (warn) | `src/cli.ts:118` | **not** the status file — its per-paper allowance lives in the ROOT key `typographyDebt`, keyed by paper path |
|
|
91
|
+
| `pipeline-check.mjs` (skill script) | `skills/paper-pipeline/scripts/pipeline-check.mjs:231-321` | the `Venue:` / `Deadline:` header line, the verdict line, the five section tables |
|
|
92
|
+
| `paper-status-gates` hook | `hooks/paper-status-gates.hook.mjs` | surfaces the verdict and unrun gates after an edit |
|
|
93
|
+
| ledger + `status.mjs` | `skills/paper-pipeline/scripts/ledger.mjs:1-25`, `status.mjs:1-7` | **nothing** from the file — they record runs in `runs.jsonl` and derive status from it |
|
|
94
|
+
|
|
95
|
+
The last row matters. `ledger.mjs` opens with: _"Until 2026-08-07 the pipeline's state lived in a
|
|
96
|
+
hand-maintained markdown table (`PIPELINE-STATUS.md`) whose rows a human ticked. It lied constantly
|
|
97
|
+
and in one direction"_, and `status.mjs`: _"Everything printed here is derived from the ledger plus
|
|
98
|
+
the bytes currently on disk. There is no field a human can set."_ So the package has already
|
|
99
|
+
decided, in code, that the scorecard tables are derivable state — while the template still asks a
|
|
100
|
+
human to copy and tick them.
|
|
101
|
+
|
|
102
|
+
## 2. What `PIPELINE-STATUS.md` actually holds — the split
|
|
103
|
+
|
|
104
|
+
Read from the template (`skills/paper-pipeline/references/pipeline-status-template.md`), the
|
|
105
|
+
fixtures (`fixtures/paper-stages/*`, `fixtures/real-markdown-paper/`) and four real files in the
|
|
106
|
+
owner's corpus. Real sizes: 86, 95, 1 057 and 1 301 lines. In the two big ones the machine-read
|
|
107
|
+
part is the first 10–25 lines of front matter; the rest is a dated journal of decisions. (No
|
|
108
|
+
content of those files is reproduced here.)
|
|
109
|
+
|
|
110
|
+
| content | kind | who should write it |
|
|
111
|
+
| ------------------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
112
|
+
| paper short name / title | **config** | human, once — `rpp new` can prefill from the folder name |
|
|
113
|
+
| venue, deadline, blind model (the `Venue:` header line) | **config** | human; changes when the paper is re-targeted |
|
|
114
|
+
| `researchQuestion` | **config** | human, once |
|
|
115
|
+
| format (`.tex` vs `.md`) | **derived** | from which source file exists |
|
|
116
|
+
| venue profile, paper kind, pdf path (a separate `venue.json` in one real paper) | **config** | human; already a per-paper file in practice |
|
|
117
|
+
| typography allowance (`typographyDebt`) | **config** | human, but it sits in the root key keyed by path today |
|
|
118
|
+
| `stages[]`: `stage`, `date`, `venue` | **state** | **tool** at the moment of freezing (`rpp freeze`, a later step) |
|
|
119
|
+
| `stages[]`: `pdf`, `bytes`, `source`, `sourceBytes` | **state (a lock)** | **tool** — derivable at freeze time, then _kept_ as an integrity record, exactly like a lockfile hash. If it were re-derived on every run, `bytesDiffer` could never fire |
|
|
120
|
+
| `stages[]`: `sourceLost: true` + its explaining comment | **state** | human — a declared exception, and the comment is the point |
|
|
121
|
+
| `State:` header field | **derived** | duplicates `stages` (the real files say so in their own comments: "the field is the carrier, the header is display") |
|
|
122
|
+
| `Updated:` | **derived** | `git log -1` on the file |
|
|
123
|
+
| which `versions/*` exist, their sizes | **derived** | the disk (`paper/stages` already lists them, `paper-stages.mjs:210`) |
|
|
124
|
+
| table columns `id`, `Work`, `Skill`, `Requires` | **derived** | the canonical edge set in `scripts/pipeline-edges.mjs` — today copied by hand and policed by a rule for having been copied wrong |
|
|
125
|
+
| table columns `Status`, `Date`, `Result`, `Open` | **state** | the ledger already records this (`ledger.mjs record …`) |
|
|
126
|
+
| "stale" marks on CONTINUOUS rows | **derived** | computed from git + ledger (`pipeline-check.mjs`, `stale-continuous`) |
|
|
127
|
+
| "Gates at a glance" line | **derived** | from the rows |
|
|
128
|
+
| readiness verdict | **state (judgement)** | human or the reviewing skill; not derivable |
|
|
129
|
+
| the journal (dated sections, reasons, what was decided) | **record** | human; never machine-read, and should not be |
|
|
130
|
+
|
|
131
|
+
The crux in one sentence: **the machine reads a small config block and a short list of stage
|
|
132
|
+
records; the human reads a long journal; and the tables in between are state the ledger already
|
|
133
|
+
owns.** Copying the template by hand makes a human produce all three, including the part the tool
|
|
134
|
+
already computes.
|
|
135
|
+
|
|
136
|
+
## 3. Prior art
|
|
137
|
+
|
|
138
|
+
### 3.1 Scaffolding a new unit from a template
|
|
139
|
+
|
|
140
|
+
| tool | command | what it creates | quote |
|
|
141
|
+
| -------------- | --------------------------------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
142
|
+
| Hugo | `hugo new content posts/x.md` | one file from an **archetype** | "An archetype is a template for new content." Lookup order: `archetypes/posts.md` → theme's → `archetypes/default.md` → theme's → built-in. `{{ .Date }}` is filled and `draft` set to `true` — [gohugo.io/content-management/archetypes](https://gohugo.io/content-management/archetypes/) |
|
|
143
|
+
| Cargo | `cargo new` / `cargo init` | a package dir with its own `Cargo.toml` | "a simple template with a `Cargo.toml` manifest, sample source file, and a VCS ignore file" — [cargo-new](https://doc.rust-lang.org/cargo/commands/cargo-new.html). And: "Running `cargo new` inside a workspace also automatically adds the newly created package to the `members` key" — [the Rust book ch. 14.3](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html) |
|
|
144
|
+
| Quarto | `quarto create project <type> <name>` | a project dir with `_quarto.yml` | "Use the `quarto create project` command to create a new project, using the prompt. Or define the type and the project name as arguments" — [quarto-projects](https://quarto.org/docs/projects/quarto-projects.html). Types include `manuscript`; a manuscript "has two files: … `index.qmd`" and `_quarto.yml` — [manuscripts/authoring](https://quarto.org/docs/manuscripts/authoring/vscode.html) |
|
|
145
|
+
| changesets | `changeset` (add) | **one file per change** in `.changeset/` | "a new changeset will be added which is a markdown file with YAML front matter" … "Once you are happy with the changeset, commit the file to your branch." — [adding-a-changeset](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md) |
|
|
146
|
+
| npm / create-* | `npm init -y`, `npm create vite`, `create-next-app` | a project | see § 5 for their non-interactive flags |
|
|
147
|
+
|
|
148
|
+
**What carries over:** every one of them creates the unit from a template **file** that lives
|
|
149
|
+
somewhere a user can override (Hugo's lookup order is the clearest). None asks the user to copy a
|
|
150
|
+
template out of a documentation page. rpp's template today lives _inside a fenced code block of a
|
|
151
|
+
reference doc_ (`pipeline-status-template.md:143-205`), so a command would have to extract it by
|
|
152
|
+
parsing markdown — the fix is to ship the template as its own file and have the doc point at it.
|
|
153
|
+
|
|
154
|
+
### 3.2 Where settings live: one root config, or one per unit?
|
|
155
|
+
|
|
156
|
+
| tool | root config | per-unit settings | how units are found |
|
|
157
|
+
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
158
|
+
| **Quarto** (closest analog) | `_quarto.yml`: "Any document rendered within the project directory will automatically inherit the metadata defined at the project level." | document YAML front matter; optional `_metadata.yml` per directory — "Document level options take priority, followed by directory options and finally project-level options." | "By default, all valid Quarto input files … in the project directory will be rendered" — no list. [quarto-projects](https://quarto.org/docs/projects/quarto-projects.html) |
|
|
159
|
+
| **Astro** content collections | one `src/content.config.ts` — "All of your build-time content collections are defined in a special src/content.config.ts file" | each entry's front matter, validated by a schema: "If any file violates its collection schema, Astro will provide a helpful error" | a glob loader over a directory. [content-collections](https://docs.astro.build/en/guides/content-collections/) |
|
|
160
|
+
| **Cargo** workspace | `[workspace] members` — "also supports globs … like `crates/*`" | each member's own `Cargo.toml` | globs **or** an explicit list, which `cargo new` edits for you. [workspaces](https://doc.rust-lang.org/cargo/reference/workspaces.html) |
|
|
161
|
+
| **Eleventy** | global data | front matter, and **directory data files**: `posts/posts.json` "will configure a layout for all of the templates inside of `posts/*`"; front matter wins | the input directory. [data-template-dir](https://www.11ty.dev/docs/data-template-dir/) |
|
|
162
|
+
| **ESLint** flat config | one `eslint.config.*` with `files` globs: "an array of glob patterns indicating the files that the configuration object should apply to" | none — per-directory cascade was removed | globs. [configuration-files](https://eslint.org/docs/latest/use/configure/configuration-files) |
|
|
163
|
+
| **Vale** | one `.vale.ini`: "A section header is a glob matched against a file's path … A file matches every section whose glob fits it" | none | globs. [.vale.ini](https://docs.vale.sh/topics/.vale.ini.md) |
|
|
164
|
+
| **markdownlint-cli2** | `.markdownlint-cli2.jsonc` in any directory: "Settings in this file apply to the directory it is in and all subdirectories" and "merge with" the parent's | the per-directory file _is_ the mechanism | the directory tree. [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) |
|
|
165
|
+
|
|
166
|
+
**What this says, without taste:**
|
|
167
|
+
|
|
168
|
+
1. **The root config names a SCOPE, not a roster.** Quarto, Astro, Eleventy, ESLint and Vale all
|
|
169
|
+
discover units from a directory or glob. Cargo is the only one with an explicit member list, and
|
|
170
|
+
it is also the only one that had to make its scaffolding command _edit_ the root file — a list
|
|
171
|
+
kept by hand drifts. rpp already discovers papers by marker (`src/build.ts:41`), so a roster in
|
|
172
|
+
`package.json` would be a second source of truth for a fact the disk already states.
|
|
173
|
+
2. **Per-unit data lives with the unit.** Quarto (document front matter / `_metadata.yml`), Astro
|
|
174
|
+
(entry front matter), Cargo (member `Cargo.toml`), Eleventy (directory data). The tools that put
|
|
175
|
+
per-unit settings in the root (ESLint `files`, Vale sections) are _linters of code_, where units
|
|
176
|
+
have no identity of their own — a paper does.
|
|
177
|
+
3. **Front matter of the source itself is not available to rpp**, because half the papers are
|
|
178
|
+
LaTeX, which has no YAML front matter. Quarto and Astro can use the document's own header only
|
|
179
|
+
because every document is markdown. So rpp needs a per-paper **file**, and it already has one:
|
|
180
|
+
`PIPELINE-STATUS.md`, whose front matter is exactly where Quarto would put these fields.
|
|
181
|
+
4. **Root-level keys that are really per-paper** (`typographyDebt: { "papers/my-paper": … }`,
|
|
182
|
+
`src/cli.ts:104`) are the ESLint-overrides style. They work, but they break silently on a folder
|
|
183
|
+
rename. Not worth moving today (one ratchet number for one rule); worth not adding more of.
|
|
184
|
+
|
|
185
|
+
### 3.3 State the tool writes vs config the human writes
|
|
186
|
+
|
|
187
|
+
| tool | human-written | tool-written | committed? |
|
|
188
|
+
| -------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
189
|
+
| release-please | `release-please-config.json` ("releaser specific configuration") | `.release-please-manifest.json` — "release-please will record a new version into the manifest file"; "Manually editing the manifest is only appropriate in the bootstrap case" | yes, both. [manifest-releaser](https://github.com/googleapis/release-please/blob/main/docs/manifest-releaser.md) |
|
|
190
|
+
| changesets | the changeset message | the file is _created_ by the command, _consumed_ by `changeset version` | yes |
|
|
191
|
+
| Quarto freeze | document + `freeze:` option | `_freeze/` — "The computational results of documents executed with `freeze` are stored in the `_freeze` directory" | "You should check the contents of `_freeze` into version control" — [code-execution](https://quarto.org/docs/projects/code-execution.html) |
|
|
192
|
+
| npm / Cargo | `package.json` / `Cargo.toml` | lockfile with integrity hashes | yes |
|
|
193
|
+
|
|
194
|
+
**Pattern:** config and state are separate things with separate writers, the state is committed,
|
|
195
|
+
and a human edits the state only to bootstrap. rpp's `stages[].bytes` is textbook lock state —
|
|
196
|
+
written once at freeze time, then checked forever — and today a human types it.
|
|
197
|
+
|
|
198
|
+
## 4. Recommendation — paper folders
|
|
199
|
+
|
|
200
|
+
### Topology
|
|
201
|
+
|
|
202
|
+
- **Root (`package.json` key): the papers directory only.** Unchanged. It stays a scope, like
|
|
203
|
+
Quarto's project dir or Astro's collection glob. No list of papers.
|
|
204
|
+
- **Per paper: the paper folder's `PIPELINE-STATUS.md` front matter** holds that paper's config
|
|
205
|
+
(`researchQuestion`, and the venue/deadline fields that today live in a prose header line) and
|
|
206
|
+
its stage records. Unchanged carrier, so every existing paper keeps working.
|
|
207
|
+
- **Not recommended now: a separate `paper.yml`.** It would split config from the journal cleanly
|
|
208
|
+
(the 1 300-line files are 98% journal), but it moves every rule's input and every consumer's
|
|
209
|
+
files for a gain nobody has hit as a bug yet. Trigger to revisit: a rule needing a per-paper
|
|
210
|
+
setting that is awkward in markdown front matter, or `venue.json` growing a second reader.
|
|
211
|
+
|
|
212
|
+
### Commands
|
|
213
|
+
|
|
214
|
+
**`rpp new <name> [--format tex|md]`** — creates `<papers>/<name>/` with:
|
|
215
|
+
|
|
216
|
+
- `paper.tex` or `paper.md` (a minimal stub; `--format` defaults to `tex` — ask when interactive);
|
|
217
|
+
- `PIPELINE-STATUS.md` from a **template file** shipped in the package, with the name filled in,
|
|
218
|
+
`researchQuestion` present but empty and commented, and **no** `stages` field (a new paper has
|
|
219
|
+
shipped nothing — `paper-stages.mjs:24`).
|
|
220
|
+
|
|
221
|
+
Rules for it, each taken from a tool above:
|
|
222
|
+
|
|
223
|
+
- the name is validated (`[a-z0-9._-]+`) — it becomes a path and, via the hooks, a shell argument;
|
|
224
|
+
- it **never overwrites**; run on an existing folder it adds only the missing required files
|
|
225
|
+
(`cargo init` semantics). That is also the migration for old folders missing the file;
|
|
226
|
+
- template lookup Hugo-style: a project override (e.g. `<papers>/.template/`) first, the package
|
|
227
|
+
default second — so the owner can keep his own richer scorecard without forking the package;
|
|
228
|
+
- it prints what it created and then runs `rpp lint <that folder>`, so the first thing the user
|
|
229
|
+
sees is a green report, not the current "missing PIPELINE-STATUS.md".
|
|
230
|
+
|
|
231
|
+
**Later step, NOT part of this change — `rpp freeze <paper> <stage> [--pdf <file>] [--date <iso>]`**,
|
|
232
|
+
the release-please move, recorded so the design is not lost:
|
|
233
|
+
|
|
234
|
+
- copies the built pdf and the source into `versions/<date>-<stage>.pdf|.tex|.md`;
|
|
235
|
+
- **appends** one record to `stages` with `stage`, `date`, `pdf`, `bytes`, `source`, `sourceBytes`;
|
|
236
|
+
- refuses a stage outside `STAGES` (`paper-stages.mjs:53`) and a date/stage pair already present.
|
|
237
|
+
|
|
238
|
+
⚠️ **Measured constraint for `freeze`: the real files carry load-bearing YAML comments inside
|
|
239
|
+
`stages` entries** (the `sourceLost` explanations). `js-yaml` (the dependency today) loses comments
|
|
240
|
+
on dump. `freeze` must therefore either append the record as text after the last entry, or use a
|
|
241
|
+
comment-preserving YAML document API — never parse-and-re-serialize.
|
|
242
|
+
|
|
243
|
+
**No command for scorecard rows.** Their status/date columns are what `ledger.mjs record` already
|
|
244
|
+
writes and `status.mjs` already derives. Converging the tables onto the ledger is a separate
|
|
245
|
+
decision with skill-text consequences; out of scope here. The scaffold keeps the tables, because
|
|
246
|
+
the skills tell agents to update them.
|
|
247
|
+
|
|
248
|
+
### Who writes what, after this
|
|
249
|
+
|
|
250
|
+
| | before | after |
|
|
251
|
+
| ------------------------------------------ | ------------------------- | ----------------------------------------------------------------------- |
|
|
252
|
+
| the folder and source stub | human | `rpp new` |
|
|
253
|
+
| `PIPELINE-STATUS.md` skeleton | human copies a code fence | `rpp new` |
|
|
254
|
+
| `researchQuestion`, venue, deadline | human | human (unchanged) |
|
|
255
|
+
| `stages` records, bytes, the frozen copies | human types byte counts | still human; `rpp freeze` is the later step that takes it over |
|
|
256
|
+
| `sourceLost` exceptions | human | human (unchanged) |
|
|
257
|
+
| `State:`, `Updated:`, `Requires` cells | human | still human; **derivable** — candidates to drop from the template later |
|
|
258
|
+
| row status | human ticks | ledger (already exists); tables unchanged for now |
|
|
259
|
+
| the journal | human | human (unchanged) |
|
|
260
|
+
|
|
261
|
+
### Migration
|
|
262
|
+
|
|
263
|
+
- **Existing folders with a `PIPELINE-STATUS.md`** (the owner's four): nothing changes. Their
|
|
264
|
+
front matter is already the per-paper carrier; a later `rpp freeze` would append to their lists.
|
|
265
|
+
- **Existing folders without one** (a draft that has only `paper.md`): `rpp new <that-name>` fills
|
|
266
|
+
the missing file and touches nothing else.
|
|
267
|
+
- **No `rpp migrate` is needed** for this recommendation. One would be needed only if the carrier
|
|
268
|
+
later moves to `paper.yml`.
|
|
269
|
+
|
|
270
|
+
### The smallest first step
|
|
271
|
+
|
|
272
|
+
Move the template out of the code fence into its own file (say
|
|
273
|
+
`templates/paper/PIPELINE-STATUS.md`, plus `paper.tex` and `paper.md` stubs), make
|
|
274
|
+
`pipeline-status-template.md` point at it instead of containing it, and add `rpp new` that copies
|
|
275
|
+
it. Then the README sentence "Nothing generates it and no command refreshes it" becomes "`rpp new`
|
|
276
|
+
creates it". No rule changes, no consumer migration. `rpp freeze` is step two; `init` offering to
|
|
277
|
+
call `rpp new` is step three (§ 5).
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## 5. Recommendation — hooks: `init` writes them, the plugin stops carrying them
|
|
282
|
+
|
|
283
|
+
### What the earlier decision said, and whether it still holds
|
|
284
|
+
|
|
285
|
+
`docs/install.md:126-127`: _"Step 3 cannot be collapsed: it is typed into a different program, and
|
|
286
|
+
nothing on disk can type it for you."_ The premise is that hooks can only reach Claude Code through
|
|
287
|
+
a plugin. **They can reach it through a file on disk**, and Claude Code documents it as the
|
|
288
|
+
first-class way to share hooks with a team:
|
|
289
|
+
|
|
290
|
+
> | `.claude/settings.json` | Single project | Yes, can be committed to the repo |
|
|
291
|
+
> — [Claude Code hooks](https://code.claude.com/docs/en/hooks)
|
|
292
|
+
|
|
293
|
+
> Commit `.claude/settings.json` so everyone who clones the repository gets the same permissions,
|
|
294
|
+
> hooks, telemetry, and plugins. — [Claude Code settings](https://code.claude.com/docs/en/settings)
|
|
295
|
+
|
|
296
|
+
> Direct edits to hooks in settings files are normally picked up automatically by the file watcher.
|
|
297
|
+
> — [hooks](https://code.claude.com/docs/en/hooks)
|
|
298
|
+
|
|
299
|
+
The reasons recorded for the plugin in `docs/install.md` § "Why the plugin ships no code" are about
|
|
300
|
+
where the **code** lives (skills and scripts ride with the npm package, because a plugin fetched
|
|
301
|
+
from npm gets no `node_modules`). They are still true, and they are not reasons for the plugin:
|
|
302
|
+
they are the reason the plugin is **empty**. Today it carries exactly one file,
|
|
303
|
+
`plugin/hooks/hooks.json`, whose three commands already point into the project's own
|
|
304
|
+
`${CLAUDE_PROJECT_DIR}/node_modules/research-paper-pipeline/bin/rpp.mjs`. Nothing in it needs a
|
|
305
|
+
plugin to exist.
|
|
306
|
+
|
|
307
|
+
### Four findings that decide it
|
|
308
|
+
|
|
309
|
+
1. **A plugin the repository declares does not load in a cloud session; `.claude/settings.json`
|
|
310
|
+
hooks do, in a single-repo session.** **[corrected]** The first draft said "plugins do not load
|
|
311
|
+
in a cloud session" — too strong: plugins installed or synced at the user's account level do
|
|
312
|
+
load there. What does not load is what the table below says, plugins declared in the
|
|
313
|
+
repository's own settings. The owner works through Claude Code on the web. The
|
|
314
|
+
cloud-environment docs, table "What carries over from your setup":
|
|
315
|
+
|
|
316
|
+
> Your repo's `.claude/settings.json` hooks and permission rules — **Yes, in a session with one repository**
|
|
317
|
+
>
|
|
318
|
+
> Plugins and marketplaces declared in your repo's `.claude/settings.json` — **No** — A cloud
|
|
319
|
+
> session doesn't install the plugins a repository turns on under `enabledPlugins`, including
|
|
320
|
+
> ones from the marketplaces it lists under `extraKnownMarketplaces`.
|
|
321
|
+
> — [cloud-environments](https://code.claude.com/docs/en/cloud-environments)
|
|
322
|
+
|
|
323
|
+
So on the owner's main surface a plugin the **repository** turns on delivers **zero** hooks, and
|
|
324
|
+
the blocking guard is off, silently; a plugin each **user** installs at account level would load,
|
|
325
|
+
but that is per person — a collaborator or a fresh account gets nothing, and nothing on disk
|
|
326
|
+
records it. This also rules out the half-way option of writing `extraKnownMarketplaces` +
|
|
327
|
+
`enabledPlugins` into the project settings.
|
|
328
|
+
|
|
329
|
+
2. **An agent cannot type `/plugin …`.** Those are slash commands for a human in the Claude Code UI.
|
|
330
|
+
An agent that installs rpp for the user (the common case now) finishes with no hooks and no way
|
|
331
|
+
to fix it. A settings-file write is an ordinary file edit any agent can make — and `rpp init`
|
|
332
|
+
can make it for them.
|
|
333
|
+
3. **`rpp doctor` cannot check the plugin** — it says so: _"the plugin (the hooks inside Claude
|
|
334
|
+
Code) cannot be checked from a terminal — type /plugin inside Claude Code"_ (`src/doctor.ts:273-274`).
|
|
335
|
+
It **can** read `.claude/settings.json`. For a guard whose success state is silence, "doctor can
|
|
336
|
+
see it" is the whole point of doctor.
|
|
337
|
+
4. **The only real consumer already does it this way.** The owner's knowledge base does not install
|
|
338
|
+
the rpp plugin (its `enabledPlugins` lists two other plugins); it wires all three rpp hooks
|
|
339
|
+
directly in `.claude/settings.json`, by hand. The installer should produce what the working
|
|
340
|
+
install already looks like.
|
|
341
|
+
|
|
342
|
+
Two further, smaller points: `settings.json` hooks come from the **same installed version** as the
|
|
343
|
+
code they call, while a marketplace plugin is fetched from the repository separately (a hook renamed
|
|
344
|
+
in one and not the other would fail to launch — plausible, not measured); and the plugin is one more
|
|
345
|
+
moving part that `install.md` has to explain.
|
|
346
|
+
|
|
347
|
+
### How other tools install hooks with zero manual steps
|
|
348
|
+
|
|
349
|
+
| tool | how hooks get installed | treatment of what it did not create | quote / source |
|
|
350
|
+
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
351
|
+
| husky | `husky init` writes `.husky/pre-commit` and edits `package.json` `prepare`, so every later `npm install` re-installs | only manages `.husky/` | `husky init` "creates a `pre-commit` script in `.husky/`" and "updates the `prepare` script in `package.json`" — [husky get started](https://typicode.github.io/husky/get-started.html) |
|
|
352
|
+
| simple-git-hooks | hooks declared in a `package.json` key; `npx simple-git-hooks` writes them; recommended in `prepare` | removes unused hooks by default (`preserveUnused` to keep) | "Now git hooks will be installed automatically when `npm install` is run" — [simple-git-hooks](https://github.com/toplenboren/simple-git-hooks) |
|
|
353
|
+
| lefthook | `lefthook install` writes git hooks from `lefthook.yml`; the npm package runs it at postinstall (pnpm needs `onlyBuiltDependencies` or it silently does not) | backs existing hooks up as `.old` | "Installs configured hooks to Git hooks." — [lefthook install](https://lefthook.dev/usage/commands/install/), [lefthook npm](https://lefthook.dev/installation/node/) |
|
|
354
|
+
| pre-commit | `pre-commit install`; never automatic on clone unless a global template dir is set | "migration mode which runs both your existing hooks and hooks for pre-commit" | "Every time you clone a project using pre-commit running `pre-commit install` should always be the first thing you do." — [pre-commit.com](https://pre-commit.com/) |
|
|
355
|
+
|
|
356
|
+
Common shape: **the tool writes the hook wiring into a file the host already reads, idempotently,
|
|
357
|
+
and preserves what it did not write.** Nobody asks the user to paste the wiring. The one twist for
|
|
358
|
+
rpp is favourable: unlike `.git/hooks/`, `.claude/settings.json` is a **committed** file, so one
|
|
359
|
+
`rpp init` wires the hooks for every clone and every cloud session — husky needs `prepare` to get
|
|
360
|
+
the same effect; rpp does not.
|
|
361
|
+
|
|
362
|
+
### The design
|
|
363
|
+
|
|
364
|
+
- **`rpp init` merges rpp's three hook commands into `<project>/.claude/settings.json`** — the
|
|
365
|
+
shared, committed file (husky's analog). `--no-hooks` skips. **[corrected]** `--hooks=local`
|
|
366
|
+
(write `.claude/settings.local.json` instead) is **deferred and not implemented**; `init` refuses
|
|
367
|
+
the flag by name rather than reading it as the directory argument.
|
|
368
|
+
- **The merge is vigiles's, and it is already public.** `mergeHooksJson` (vigiles
|
|
369
|
+
`src/hook-install.ts:292`) is not in vigiles's `exports` map, but it is reachable as
|
|
370
|
+
`claudeCodeHookProtocol.mergeRegistrations(existing, compiled, managedBy)` from the exported
|
|
371
|
+
`vigiles/claude-code` entry (`src/adapters/claude-code/hook-protocol.ts:64-70`, re-exported at
|
|
372
|
+
`src/claude-code.ts:66`). rpp already depends on vigiles. (Re-checked against the installed
|
|
373
|
+
31.0.0 before building on it: the export and the signature
|
|
374
|
+
`mergeRegistrations(existing, compiled, managedBy)` are unchanged.) Probe run 2026-09-23 against
|
|
375
|
+
the installed vigiles 30.0.2, with `compiled` = the three entries from `plugin/hooks/hooks.json` and
|
|
376
|
+
`managedBy = "node_modules/research-paper-pipeline/bin/rpp.mjs"`, over a settings object holding
|
|
377
|
+
a user hook in the same `Edit|Write|MultiEdit` matcher plus a `permissions` block:
|
|
378
|
+
|
|
379
|
+
```
|
|
380
|
+
rpp commands after 1st merge: 3 after 2nd: 3
|
|
381
|
+
idempotent: true
|
|
382
|
+
user hook kept: true permissions kept: true
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Ownership is decided per **command** by the path token (quotes and `${CLAUDE_PROJECT_DIR}/`
|
|
386
|
+
stripped), so a user's command sharing a matcher block survives — the defect that merge was
|
|
387
|
+
written to fix. Because all three rpp commands share one path token, a hook renamed or removed in
|
|
388
|
+
a later version is cleaned up by the next `init` (simple-git-hooks' "unused hooks removed").
|
|
389
|
+
|
|
390
|
+
- **`hooks.json` stays the one source of the wiring**; `init` reads it rather than a second copy.
|
|
391
|
+
- **Refuse to duplicate a hook wired under another spelling.** The owner's knowledge base wires the
|
|
392
|
+
same three hooks through `vigiles … hook-runtime run-program …/hooks/<name>.hook.mjs`, not through
|
|
393
|
+
`bin/rpp.mjs`. The merge above would not recognise those as rpp's and would add a second copy —
|
|
394
|
+
and Claude Code dedupes only _identical_ handlers ("If you define the same handler in more than
|
|
395
|
+
one settings file, it runs once. A plugin's or skill's copy of the same handler stays separate").
|
|
396
|
+
So before merging, `init` checks every existing command for the three hook **names** under
|
|
397
|
+
`node_modules/research-paper-pipeline/`; if any is found under a different spelling, it reports
|
|
398
|
+
"already wired" and writes nothing.
|
|
399
|
+
- **`doctor` reads the file** and reports: wired / wired twice / not wired / wired via the plugin
|
|
400
|
+
as well (the project's `enabledPlugins` can show the last one; a user-scope plugin it cannot see,
|
|
401
|
+
and it should say so).
|
|
402
|
+
- **The plugin**: deprecate as the hook carrier. Keep the marketplace entry for one release so
|
|
403
|
+
existing plugin users are not broken; `init` and `doctor` tell a plugin user to
|
|
404
|
+
`/plugin uninstall`, because plugin + settings would run every hook twice (the docs quote above).
|
|
405
|
+
The `/plugin` lines disappear from `nextSteps` (`src/init.ts:252-265`).
|
|
406
|
+
|
|
407
|
+
**What this does not solve, named so it is not mistaken for solved:**
|
|
408
|
+
|
|
409
|
+
- A **multi-repository** cloud session reads neither plugins nor `settings.json` hooks from the
|
|
410
|
+
repositories ("starts above the clones and reads only the `enabledPlugins` and
|
|
411
|
+
`extraKnownMarketplaces` keys … not permission rules, hooks" — [settings](https://code.claude.com/docs/en/settings)).
|
|
412
|
+
No install route reaches it; that is the platform, not rpp.
|
|
413
|
+
- A **fresh clone before `npm install`**: the hook command names `node_modules/…/rpp.mjs`, which
|
|
414
|
+
does not exist yet. This is identical under the plugin. vigiles has an explicit policy for it
|
|
415
|
+
(`hookRuntimeMissingExit`: a gate fails closed, a nudge exits 0, `src/hook-install.ts:147-183`);
|
|
416
|
+
whether rpp's emitted commands should adopt it is a separate question.
|
|
417
|
+
|
|
418
|
+
## 6. `rpp init` in two modes — a human at a terminal, or an agent/CI
|
|
419
|
+
|
|
420
|
+
### The convention, from three sources
|
|
421
|
+
|
|
422
|
+
- clig.dev: _"Only use prompts or interactive elements if `stdin` is an interactive terminal (a
|
|
423
|
+
TTY)."_ · _"If `--no-input` is passed, don't prompt or do anything interactive."_ · _"Never
|
|
424
|
+
*require* a prompt. Always provide a way of passing input with flags or arguments."_ —
|
|
425
|
+
[clig.dev](https://clig.dev/)
|
|
426
|
+
- npm: `npm init -y` / `--yes` — _"skip the questionnaire altogether"_ —
|
|
427
|
+
[npm-init](https://docs.npmjs.com/cli/v10/commands/npm-init)
|
|
428
|
+
- Vite: _"To create a project without interactive prompts, you can use the `--no-interactive`
|
|
429
|
+
flag"_, and every answer has a flag (`--template vue`) — [vite guide](https://vite.dev/guide/)
|
|
430
|
+
- create-next-app: `--yes` — _"Use previous preferences or defaults for all options"_, and each
|
|
431
|
+
prompt has a matching flag — [create-next-app](https://nextjs.org/docs/app/api-reference/cli/create-next-app)
|
|
432
|
+
|
|
433
|
+
`init` already follows half of this: it asks only when `process.stdin.isTTY`
|
|
434
|
+
(`src/init.ts:355`) and takes a stated default otherwise. Missing: a `--yes` flag, a check of
|
|
435
|
+
**stdout** too (an agent that pipes output has a TTY-less stdout), and the `CI` environment
|
|
436
|
+
variable. Proposed rule: _interactive = stdin is a TTY **and** stdout is a TTY **and** `CI` is
|
|
437
|
+
unset **and** no `--yes`._
|
|
438
|
+
|
|
439
|
+
### What `init` asks, and what it does without asking
|
|
440
|
+
|
|
441
|
+
| decision | human at a terminal | agent / CI / `--yes` | flag |
|
|
442
|
+
| ----------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | --------------------------------------- |
|
|
443
|
+
| papers directory | asked only if several candidates (today) | first candidate, said so (today) | `init <dir>` then the key |
|
|
444
|
+
| **hooks** | "Wire the three paper hooks into `.claude/settings.json` (committed, shared)? **[Y/n]**" | **yes** — the guard is what the package is for, the edit is idempotent and shows up in `git diff` | `--no-hooks` (`--hooks=local` deferred) |
|
|
445
|
+
| CI workflow | "Add a GitHub Actions workflow? **[y/N]**" (today) | **no** (today) — it writes into `.github/` and spends Actions minutes, which are metered | `--ci` / `--no-ci` |
|
|
446
|
+
| first paper | only when the papers dir holds no paper: "Create a first paper? name: [skip]" then "format: tex / md [tex]" | **no**, unless `--paper <name>` is given | `--paper <name> --format tex\|md` |
|
|
447
|
+
| external programs | reported (today) | reported (today) | — |
|
|
448
|
+
|
|
449
|
+
Why the hooks default is "yes" even without asking, while CI is "no": the rule `install.md` already
|
|
450
|
+
adopted from Playwright is _"ask only about what cannot be guessed or is expensive"_. Installing a
|
|
451
|
+
package whose purpose includes an edit guard makes "yes" guessable; a reviewable, idempotent edit
|
|
452
|
+
to one committed file is cheap. A workflow costs money on every push. husky's `init` edits
|
|
453
|
+
`package.json` without a single prompt for the same reason.
|
|
454
|
+
|
|
455
|
+
Every line `init` prints for a default it took names the flag that changes it — that output is
|
|
456
|
+
what an agent reads instead of a separate agent manual (next section).
|
|
457
|
+
|
|
458
|
+
## 7. One README or two?
|
|
459
|
+
|
|
460
|
+
**One README, and the difference between agent and human installs lives in the CLI's TTY
|
|
461
|
+
behaviour** — the coordinator's position holds. AGENTS.md defines itself as the file for agents
|
|
462
|
+
working **on** a repository: _"README.md files are for humans: quick starts, project descriptions,
|
|
463
|
+
and contribution guidelines. AGENTS.md complements this by containing the extra … context coding
|
|
464
|
+
agents need: build steps, tests, and conventions"_ ([agents.md](https://agents.md/)). An agent
|
|
465
|
+
_installing_ rpp into another project never reads rpp's AGENTS.md or CLAUDE.md; it reads the README
|
|
466
|
+
and the output of `rpp init`. llms.txt is likewise an index that _points at_ the human pages rather
|
|
467
|
+
than replacing them — _"The detail lives behind the links, and is fetched only when needed"_
|
|
468
|
+
([llmstxt.org](https://llmstxt.org/)). The one related move worth noting is create-next-app's
|
|
469
|
+
`--agents-md` (default on): it writes an AGENTS.md **into the new project**, i.e. into the consumer,
|
|
470
|
+
where that project's agents will read it. That is a possible later feature for `rpp init`, not an
|
|
471
|
+
argument for a second README here.
|
|
472
|
+
|
|
473
|
+
## 8. What the build did differently from this text
|
|
474
|
+
|
|
475
|
+
Recorded so the design and the code do not quietly disagree.
|
|
476
|
+
|
|
477
|
+
- **`rpp freeze` and `--hooks=local`: not built** (see the status block at the top).
|
|
478
|
+
- **The "another spelling" check covers more spellings than § 5 named.** Besides a
|
|
479
|
+
`…/research-paper-pipeline/hooks/<name>.hook.mjs` path it recognises `npx rpp hook <name>`,
|
|
480
|
+
`node_modules/.bin/rpp hook <name>`, and `rpp.mjs` reached by an **absolute** path — vigiles'
|
|
481
|
+
merge owns only the relative `node_modules/research-paper-pipeline/bin/rpp.mjs` token, so each of
|
|
482
|
+
those would otherwise be duplicated. A hand-wired project that is missing some hooks gets them
|
|
483
|
+
named, and nothing written; `doctor` then gives that remedy instead of "run `rpp init`", which
|
|
484
|
+
would loop.
|
|
485
|
+
- **The hook names are derived from `plugin/hooks/hooks.json`**, not listed a second time.
|
|
486
|
+
- **A second `init` does not rewrite the file at all** when the merge changes nothing — so an
|
|
487
|
+
already-wired file in the user's own formatting stays byte-identical, not only equal as JSON.
|
|
488
|
+
- **`rpp doctor`'s hooks section is advisory** (it never changes the exit code): `--no-hooks` is a
|
|
489
|
+
legitimate choice, and "wired twice" is a warning with the count. It reads
|
|
490
|
+
`.claude/settings.json` only; a user-scope plugin it cannot see, and it says so.
|
|
491
|
+
- **The template lookup is per FILE**, not per directory: a project may override only
|
|
492
|
+
`PIPELINE-STATUS.md` and still get the package's source stub. `{{name}}` is the one substitution.
|
|
493
|
+
- **A name starting with a dot is refused**, on top of the `[a-z0-9._-]+` charset: discovery skips
|
|
494
|
+
dot-directories, so such a paper would be created and never linted. The same rule is why the
|
|
495
|
+
override lives at `<papers>/.template/`; `rpp lint` additionally ignores that directory for
|
|
496
|
+
ESLint (flat config does not skip dot-directories by default), and `detectPapers` no longer
|
|
497
|
+
counts a `.template` child as a paper.
|
|
498
|
+
- **`rpp new` lints the new folder itself** (`rpp lint <papers>/<name>`), so the structure check —
|
|
499
|
+
which looks at the subdirectories of the path it is given — does not run there; the folder was
|
|
500
|
+
just made with the required files, so there is nothing for it to find.
|
|
501
|
+
- **Interactive `init` asks "Create a first paper? name: [skip]" and then the format** only when the
|
|
502
|
+
name was given and `--format` was not.
|
|
503
|
+
|
|
504
|
+
## Sources checked
|
|
505
|
+
|
|
506
|
+
Fetched 2026-09-23:
|
|
507
|
+
[Hugo archetypes](https://gohugo.io/content-management/archetypes/) ·
|
|
508
|
+
[cargo new](https://doc.rust-lang.org/cargo/commands/cargo-new.html) ·
|
|
509
|
+
[Cargo workspaces](https://doc.rust-lang.org/cargo/reference/workspaces.html) ·
|
|
510
|
+
[Rust book 14.3](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html) ·
|
|
511
|
+
[Quarto projects](https://quarto.org/docs/projects/quarto-projects.html) ·
|
|
512
|
+
[Quarto manuscripts](https://quarto.org/docs/manuscripts/authoring/vscode.html) ·
|
|
513
|
+
[Quarto freeze](https://quarto.org/docs/projects/code-execution.html) ·
|
|
514
|
+
[Astro content collections](https://docs.astro.build/en/guides/content-collections/) ·
|
|
515
|
+
[Eleventy directory data](https://www.11ty.dev/docs/data-template-dir/) ·
|
|
516
|
+
[ESLint config files](https://eslint.org/docs/latest/use/configure/configuration-files) ·
|
|
517
|
+
[Vale .vale.ini](https://docs.vale.sh/topics/.vale.ini.md) ·
|
|
518
|
+
[markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) ·
|
|
519
|
+
[changesets](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md) ·
|
|
520
|
+
[release-please manifest](https://github.com/googleapis/release-please/blob/main/docs/manifest-releaser.md) ·
|
|
521
|
+
[Claude Code hooks](https://code.claude.com/docs/en/hooks) ·
|
|
522
|
+
[Claude Code settings](https://code.claude.com/docs/en/settings) ·
|
|
523
|
+
[Claude Code cloud environments](https://code.claude.com/docs/en/cloud-environments) ·
|
|
524
|
+
[husky](https://typicode.github.io/husky/get-started.html) ·
|
|
525
|
+
[simple-git-hooks](https://github.com/toplenboren/simple-git-hooks) ·
|
|
526
|
+
[lefthook install](https://lefthook.dev/usage/commands/install/) ·
|
|
527
|
+
[lefthook npm](https://lefthook.dev/installation/node/) ·
|
|
528
|
+
[pre-commit](https://pre-commit.com/) ·
|
|
529
|
+
[clig.dev](https://clig.dev/) ·
|
|
530
|
+
[npm init](https://docs.npmjs.com/cli/v10/commands/npm-init) ·
|
|
531
|
+
[Vite guide](https://vite.dev/guide/) ·
|
|
532
|
+
[create-next-app](https://nextjs.org/docs/app/api-reference/cli/create-next-app) ·
|
|
533
|
+
[AGENTS.md](https://agents.md/) ·
|
|
534
|
+
[llms.txt](https://llmstxt.org/)
|
|
535
|
+
|
|
536
|
+
Not verified from a primary page: whether Cargo skips editing `members` when a glob already covers
|
|
537
|
+
the new path (the Rust book states the auto-add; the glob case was not found), and lefthook's `.old`
|
|
538
|
+
backup (seen only in search snippets; the fetched `install` page does not state it).
|