@thedecipherist/claude-packs 0.0.0-stage → 1.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/CHANGELOG.md +680 -0
- package/LICENSE +21 -0
- package/README.md +316 -2
- package/bin/claude-packs.mjs +204 -0
- package/configurations/base/config.json +85 -0
- package/configurations/cpp/config.json +92 -0
- package/configurations/full-stack/config.json +152 -0
- package/configurations/go/config.json +96 -0
- package/configurations/node/config.json +100 -0
- package/configurations/python/config.json +97 -0
- package/configurations/synth-creator/config.json +148 -0
- package/global/hooks/gnu-shell.mjs +131 -0
- package/global/hooks/lib/program-read.mjs +196 -0
- package/global/hooks/lib/program-writes.mjs +177 -0
- package/global/hooks/lib/shell-writes.mjs +293 -0
- package/global/hooks/shell-traps.mjs +79 -0
- package/global/hooks/version-pin-guard.mjs +280 -0
- package/global/skills/latest-versions/SKILL.md +49 -0
- package/lib/backup.mjs +34 -0
- package/lib/claude-md.mjs +58 -0
- package/lib/config-cli.mjs +36 -0
- package/lib/configs.mjs +145 -0
- package/lib/coverage.mjs +86 -0
- package/lib/custom-packs.mjs +93 -0
- package/lib/doctor.mjs +115 -0
- package/lib/fix-plan.mjs +71 -0
- package/lib/git-tokens.mjs +64 -0
- package/lib/global.mjs +125 -0
- package/lib/hook-proof.mjs +28 -0
- package/lib/hook-timing.mjs +42 -0
- package/lib/hover-gate.mjs +29 -0
- package/lib/kit-lock.mjs +149 -0
- package/lib/layout.mjs +75 -0
- package/lib/logs.mjs +69 -0
- package/lib/manifest.mjs +24 -0
- package/lib/new.mjs +49 -0
- package/lib/once-only-report.mjs +54 -0
- package/lib/pack-manifest.mjs +20 -0
- package/lib/packs.mjs +107 -0
- package/lib/planner.mjs +59 -0
- package/lib/repeats.mjs +65 -0
- package/lib/repo-weight.mjs +28 -0
- package/lib/rules.mjs +40 -0
- package/lib/scan-detect.mjs +94 -0
- package/lib/scan.mjs +168 -0
- package/lib/selftest-cache.mjs +39 -0
- package/lib/settings.mjs +138 -0
- package/lib/shape-cli.mjs +76 -0
- package/lib/shape.mjs +4 -0
- package/lib/suggest.mjs +18 -0
- package/lib/switches.mjs +44 -0
- package/lib/symmetry-doctor.mjs +26 -0
- package/lib/sync.mjs +130 -0
- package/package.json +34 -4
- package/packs/ai/checks/boundary-computes-its-own-hash.json +30 -0
- package/packs/ai/checks/model-call-needs-boundary.json +29 -0
- package/packs/ai/checks/model-id-from-config.json +28 -0
- package/packs/ai/checks/model-output-validated.json +26 -0
- package/packs/ai/pack.json +38 -0
- package/packs/ai/rules/ai-input-sha-boundary.md +31 -0
- package/packs/ai/rules/llm-pipeline-security.md +26 -0
- package/packs/audio-core/checks/denormals-not-in-prepare.json +12 -0
- package/packs/audio-core/checks/dsp-without-apvts.json +12 -0
- package/packs/audio-core/checks/float-phase-or-rate.json +12 -0
- package/packs/audio-core/checks/juce-snap-to-zero-off.json +12 -0
- package/packs/audio-core/checks/keep-werror.json +12 -0
- package/packs/audio-core/checks/no-fast-math.json +14 -0
- package/packs/audio-core/checks/no-locale-change.json +12 -0
- package/packs/audio-core/checks/no-march-native.json +14 -0
- package/packs/audio-core/clang-tidy +28 -0
- package/packs/audio-core/hooks/lib/realtime-checks.mjs +56 -0
- package/packs/audio-core/hooks/param-id-lock.mjs +129 -0
- package/packs/audio-core/hooks/realtime-guard.mjs +67 -0
- package/packs/audio-core/pack.json +187 -0
- package/packs/audio-core/rules/audio-thread-realtime-safe.md +27 -0
- package/packs/audio-core/rules/audio-ui-thread-handoff.md +21 -0
- package/packs/audio-core/rules/boundary-tests-cross-the-boundary.md +16 -0
- package/packs/audio-core/rules/build-flags-safe.md +20 -0
- package/packs/audio-core/rules/cached-parameter-pointers.md +28 -0
- package/packs/audio-core/rules/clang-tidy-core-guidelines.md +21 -0
- package/packs/audio-core/rules/control-reaches-every-surface.md +23 -0
- package/packs/audio-core/rules/cpu-worst-case-per-block.md +26 -0
- package/packs/audio-core/rules/dc-block-after-asymmetry.md +22 -0
- package/packs/audio-core/rules/discrete-params-snap.md +19 -0
- package/packs/audio-core/rules/double-where-precision-matters.md +20 -0
- package/packs/audio-core/rules/filters-stable-under-modulation.md +29 -0
- package/packs/audio-core/rules/host-parameters-at-scale.md +24 -0
- package/packs/audio-core/rules/house-control-behaviours.md +34 -0
- package/packs/audio-core/rules/math-domain-guards.md +19 -0
- package/packs/audio-core/rules/modulated-delay-interpolation.md +22 -0
- package/packs/audio-core/rules/multiband-phase-coherent.md +22 -0
- package/packs/audio-core/rules/nonlinear-stages-antialiased.md +28 -0
- package/packs/audio-core/rules/note-ledger-and-pedals.md +22 -0
- package/packs/audio-core/rules/one-formula-per-value.md +20 -0
- package/packs/audio-core/rules/optimisations-are-null-tested.md +23 -0
- package/packs/audio-core/rules/oscillators-bandlimited.md +23 -0
- package/packs/audio-core/rules/output-limiter.md +23 -0
- package/packs/audio-core/rules/parameter-ids-never-change.md +20 -0
- package/packs/audio-core/rules/params-and-state-thread-safe.md +19 -0
- package/packs/audio-core/rules/params-snapshot-per-block.md +21 -0
- package/packs/audio-core/rules/patch-excludes-performance-controls.md +18 -0
- package/packs/audio-core/rules/per-sample-code-inlinable.md +22 -0
- package/packs/audio-core/rules/prepare-is-repeatable.md +23 -0
- package/packs/audio-core/rules/recursive-state-nan-guard.md +20 -0
- package/packs/audio-core/rules/reverb-loop-gain-bounded.md +24 -0
- package/packs/audio-core/rules/sample-accurate-midi.md +22 -0
- package/packs/audio-core/rules/sample-rate-and-block-size-from-host.md +23 -0
- package/packs/audio-core/rules/scoped-no-denormals.md +26 -0
- package/packs/audio-core/rules/smooth-continuous-parameters.md +22 -0
- package/packs/audio-core/rules/state-load-versioned-and-safe.md +22 -0
- package/packs/audio-core/rules/straight-on-and-aligned.md +21 -0
- package/packs/audio-core/rules/ui-matches-reference-look.md +21 -0
- package/packs/audio-core/rules/undo-one-gesture-one-step.md +21 -0
- package/packs/audio-core/rules/voice-steal-fades.md +21 -0
- package/packs/audio-core/rules/voice-sum-headroom.md +20 -0
- package/packs/audio-core/skills/analyse-reference-look/SKILL.md +21 -0
- package/packs/audio-core/skills/copy-and-prove/SKILL.md +98 -0
- package/packs/audio-core/skills/juce-realtime-audit/SKILL.md +22 -0
- package/packs/audio-core/skills/measure-tone/SKILL.md +20 -0
- package/packs/audio-core/skills/new-panel-control/SKILL.md +15 -0
- package/packs/audio-core/skills/prove-control/SKILL.md +23 -0
- package/packs/audio-core/skills/trace-parameter/SKILL.md +29 -0
- package/packs/audio-core/tools/lib/spectrum.mjs +112 -0
- package/packs/audio-core/tools/lib/wav.mjs +41 -0
- package/packs/audio-core/tools/tone-report.mjs +47 -0
- package/packs/base/checks/field-map-over-if-runs.json +25 -0
- package/packs/base/checks/lookup-over-if-chains.json +25 -0
- package/packs/base/checks/lookup-over-ternary-chains.json +20 -0
- package/packs/base/checks/no-code-after-midline-comment.json +12 -0
- package/packs/base/checks/one-walk-per-template.json +23 -0
- package/packs/base/checks/stub-reports-success.json +31 -0
- package/packs/base/checks/test-skip-on-failed-setup.json +17 -0
- package/packs/base/hooks/brief-guard.mjs +96 -0
- package/packs/base/hooks/code-shape.mjs +95 -0
- package/packs/base/hooks/dispatch-once.mjs +87 -0
- package/packs/base/hooks/file-size-guard.mjs +61 -0
- package/packs/base/hooks/folder-law.mjs +74 -0
- package/packs/base/hooks/git-guard.mjs +289 -0
- package/packs/base/hooks/lesson-checks.mjs +174 -0
- package/packs/base/hooks/lib/apply-edit.mjs +20 -0
- package/packs/base/hooks/lib/code-shapes.mjs +137 -0
- package/packs/base/hooks/lib/content.mjs +14 -0
- package/packs/base/hooks/lib/cpp-scan.mjs +94 -0
- package/packs/base/hooks/lib/dup-index.mjs +199 -0
- package/packs/base/hooks/lib/dup.mjs +127 -0
- package/packs/base/hooks/lib/elements.mjs +87 -0
- package/packs/base/hooks/lib/event-log.mjs +67 -0
- package/packs/base/hooks/lib/function-shapes.mjs +90 -0
- package/packs/base/hooks/lib/git-adds.mjs +20 -0
- package/packs/base/hooks/lib/git-guard-selftest.mjs +99 -0
- package/packs/base/hooks/lib/glob.mjs +33 -0
- package/packs/base/hooks/lib/hook-log.mjs +100 -0
- package/packs/base/hooks/lib/house.py +77 -0
- package/packs/base/hooks/lib/languages.mjs +66 -0
- package/packs/base/hooks/lib/layout.mjs +54 -0
- package/packs/base/hooks/lib/making-check-selftest.mjs +109 -0
- package/packs/base/hooks/lib/near-copies.mjs +76 -0
- package/packs/base/hooks/lib/program-read.mjs +196 -0
- package/packs/base/hooks/lib/program-writes.mjs +177 -0
- package/packs/base/hooks/lib/project.mjs +81 -0
- package/packs/base/hooks/lib/prose.mjs +11 -0
- package/packs/base/hooks/lib/pushed.mjs +201 -0
- package/packs/base/hooks/lib/repo-index.mjs +52 -0
- package/packs/base/hooks/lib/secret-patterns.mjs +18 -0
- package/packs/base/hooks/lib/selftest.mjs +45 -0
- package/packs/base/hooks/lib/shape-code.mjs +126 -0
- package/packs/base/hooks/lib/shape.mjs +294 -0
- package/packs/base/hooks/lib/shell-writes-selftest.mjs +65 -0
- package/packs/base/hooks/lib/shell-writes.mjs +293 -0
- package/packs/base/hooks/lib/turn.mjs +81 -0
- package/packs/base/hooks/lib/writes.mjs +71 -0
- package/packs/base/hooks/making-check.mjs +243 -0
- package/packs/base/hooks/making.mjs +106 -0
- package/packs/base/hooks/once-only.mjs +150 -0
- package/packs/base/hooks/placement-law.mjs +84 -0
- package/packs/base/hooks/repo-litter.mjs +75 -0
- package/packs/base/hooks/reuse-hint.mjs +75 -0
- package/packs/base/hooks/run-hooks.mjs +132 -0
- package/packs/base/hooks/secrets-guard.mjs +99 -0
- package/packs/base/hooks/shape-law.mjs +133 -0
- package/packs/base/hooks/suite-once.mjs +80 -0
- package/packs/base/hooks/template-law.mjs +148 -0
- package/packs/base/hooks/untrusted-boundary.mjs +53 -0
- package/packs/base/pack.json +214 -0
- package/packs/base/rules/a-control-is-seen-to-fire.md +27 -0
- package/packs/base/rules/behaviour-lives-with-its-kind.md +51 -0
- package/packs/base/rules/bug-fix-starts-red.md +25 -0
- package/packs/base/rules/field-map-over-if-runs.md +47 -0
- package/packs/base/rules/finished-work-lands.md +17 -0
- package/packs/base/rules/git-right-first-time.md +19 -0
- package/packs/base/rules/instances-by-index-identities-by-id.md +54 -0
- package/packs/base/rules/lookup-over-if-chains.md +49 -0
- package/packs/base/rules/making-line-first.md +14 -0
- package/packs/base/rules/open-items-tagged.md +17 -0
- package/packs/base/rules/plan-and-logs.md +22 -0
- package/packs/base/rules/references-use-anchors.md +19 -0
- package/packs/base/rules/refusals-are-judged.md +18 -0
- package/packs/base/rules/reuse-before-writing.md +15 -0
- package/packs/base/rules/secrets-never-committed.md +16 -0
- package/packs/base/rules/skill-writing.md +23 -0
- package/packs/base/rules/split-before-the-limit.md +14 -0
- package/packs/base/rules/subagent-dispatch.md +26 -0
- package/packs/base/rules/test-only-what-changed.md +17 -0
- package/packs/base/rules/test-the-template-once.md +22 -0
- package/packs/base/rules/untrusted-content-boundary.md +15 -0
- package/packs/base/skills/add-lesson-check/SKILL.md +26 -0
- package/packs/base/skills/fix-from-scan/SKILL.md +25 -0
- package/packs/base/skills/land-branch/SKILL.md +24 -0
- package/packs/base/skills/make-it-a-template/SKILL.md +23 -0
- package/packs/base/skills/pdf-to-md/SKILL.md +25 -0
- package/packs/base/skills/pick-tests/SKILL.md +20 -0
- package/packs/base/skills/pressure-test-skill/SKILL.md +23 -0
- package/packs/base/skills/prove-instances-identical/SKILL.md +37 -0
- package/packs/base/tools/shape-symmetry-selftest.mjs +66 -0
- package/packs/base/tools/shape-symmetry.mjs +178 -0
- package/packs/base/tools/test-verdict.mjs +75 -0
- package/packs/ci/checks/ci-permissions-stated.json +18 -0
- package/packs/ci/checks/ci-tools-pinned.json +14 -0
- package/packs/ci/pack.json +21 -0
- package/packs/ci/rules/ci-covers-what-ships.md +20 -0
- package/packs/ci/rules/generated-files-have-no-diff.md +15 -0
- package/packs/content-site/checks/captcha-is-not-constant.json +27 -0
- package/packs/content-site/checks/img-has-size.json +20 -0
- package/packs/content-site/checks/page-head-complete.json +26 -0
- package/packs/content-site/pack.json +22 -0
- package/packs/content-site/rules/content-has-one-source.md +23 -0
- package/packs/content-site/rules/media-is-sized.md +24 -0
- package/packs/content-site/rules/page-head-is-complete.md +26 -0
- package/packs/content-site/rules/public-forms-are-defended.md +27 -0
- package/packs/content-site/skills/publish-a-page/SKILL.md +21 -0
- package/packs/cpp/checks/cpp-clamp-before-cast.json +16 -0
- package/packs/cpp/checks/cpp-find-with-the-library.json +12 -0
- package/packs/cpp/checks/cpp-hardened-test-build.json +18 -0
- package/packs/cpp/checks/cpp-m-pi-not-standard.json +23 -0
- package/packs/cpp/checks/cpp-no-atoi.json +16 -0
- package/packs/cpp/checks/cpp-no-const-cast.json +16 -0
- package/packs/cpp/checks/cpp-no-float-loop-counter.json +16 -0
- package/packs/cpp/checks/cpp-no-getenv-in-product.json +22 -0
- package/packs/cpp/checks/cpp-no-mutable-global.json +20 -0
- package/packs/cpp/checks/cpp-no-mutable-local-static.json +23 -0
- package/packs/cpp/checks/cpp-no-unchecked-fwrite.json +17 -0
- package/packs/cpp/checks/cpp-no-using-namespace-in-header.json +19 -0
- package/packs/cpp/checks/cpp-no-zero-size-array.json +23 -0
- package/packs/cpp/checks/cpp-numbers-pi.json +16 -0
- package/packs/cpp/pack.json +22 -0
- package/packs/cpp/rules/cpp-absent-and-failed-are-types.md +24 -0
- package/packs/cpp/rules/cpp-builds-everywhere.md +26 -0
- package/packs/cpp/rules/cpp-faults-only-in-the-test-build.md +21 -0
- package/packs/cpp/rules/cpp-find-with-the-library.md +19 -0
- package/packs/cpp/rules/cpp-gates-share-one-reader.md +22 -0
- package/packs/cpp/rules/cpp-headers-declare.md +22 -0
- package/packs/cpp/rules/cpp-numbers-and-casts.md +22 -0
- package/packs/cpp/rules/cpp-repeats-become-templates.md +33 -0
- package/packs/cpp/rules/cpp-static-analysis-runs.md +22 -0
- package/packs/cpp/rules/cpp-thread-contracts-are-code.md +23 -0
- package/packs/cpp/skills/cpp-extract-template/SKILL.md +38 -0
- package/packs/docker/checks/alpine-with-native-build.json +13 -0
- package/packs/docker/checks/compose-db-needs-auth.json +14 -0
- package/packs/docker/checks/compose-image-pinned.json +14 -0
- package/packs/docker/checks/compose-no-hardcoded-ip.json +12 -0
- package/packs/docker/checks/compose-no-host-bind-for-data.json +18 -0
- package/packs/docker/checks/compose-no-plaintext-secret.json +13 -0
- package/packs/docker/checks/compose-no-swarm-ignored-keys.json +13 -0
- package/packs/docker/checks/compose-overlay-not-bridge.json +13 -0
- package/packs/docker/checks/compose-service-needs-healthcheck.json +11 -0
- package/packs/docker/checks/compose-service-needs-init.json +11 -0
- package/packs/docker/checks/copy-needs-chown-with-user.json +17 -0
- package/packs/docker/checks/copy-source-before-deps.json +13 -0
- package/packs/docker/checks/docker-exec-form.json +16 -0
- package/packs/docker/checks/dockerfile-needs-healthcheck.json +17 -0
- package/packs/docker/checks/dockerfile-needs-user.json +21 -0
- package/packs/docker/checks/dockerignore-has-the-basics.json +21 -0
- package/packs/docker/checks/gitattributes-forces-lf.json +17 -0
- package/packs/docker/checks/health-checks-nothing.json +25 -0
- package/packs/docker/checks/no-crlf-in-scripts.json +15 -0
- package/packs/docker/checks/no-latest-base.json +13 -0
- package/packs/docker/checks/no-secret-in-image.json +13 -0
- package/packs/docker/checks/no-sensitive-file-copy.json +13 -0
- package/packs/docker/checks/node-env-before-install.json +13 -0
- package/packs/docker/checks/npm-ci-not-install.json +13 -0
- package/packs/docker/pack.json +46 -0
- package/packs/docker/rules/compose-pinned-and-closed.md +21 -0
- package/packs/docker/rules/compose-swarm-safe.md +26 -0
- package/packs/docker/rules/docker-exec-form.md +19 -0
- package/packs/docker/rules/dockerfile-multistage.md +13 -0
- package/packs/docker/rules/dockerfile-production.md +25 -0
- package/packs/docker/skills/docker-diagnose/SKILL.md +23 -0
- package/packs/dynamic-data/checks/draft-on-the-server.json +27 -0
- package/packs/dynamic-data/checks/guard-does-its-job.json +28 -0
- package/packs/dynamic-data/checks/key-is-not-a-display-name.json +28 -0
- package/packs/dynamic-data/checks/no-code-from-data.json +29 -0
- package/packs/dynamic-data/checks/no-collection-from-request.json +27 -0
- package/packs/dynamic-data/checks/no-pipeline-from-stored-text.json +27 -0
- package/packs/dynamic-data/checks/no-regexp-from-stored-config.json +27 -0
- package/packs/dynamic-data/checks/no-template-from-stored-text.json +27 -0
- package/packs/dynamic-data/checks/save-takes-named-paths.json +27 -0
- package/packs/dynamic-data/pack.json +23 -0
- package/packs/dynamic-data/rules/a-list-is-a-saved-query.md +23 -0
- package/packs/dynamic-data/rules/definitions-are-validated-data.md +24 -0
- package/packs/dynamic-data/rules/every-dynamic-surface-has-a-boundary.md +29 -0
- package/packs/dynamic-data/rules/fields-have-owners.md +22 -0
- package/packs/dynamic-data/rules/imports-drop-nothing.md +26 -0
- package/packs/dynamic-data/rules/one-definition-many-surfaces.md +24 -0
- package/packs/dynamic-data/rules/one-fact-one-place.md +28 -0
- package/packs/dynamic-data/rules/rules-live-in-the-engine.md +29 -0
- package/packs/dynamic-data/rules/saves-are-patches.md +28 -0
- package/packs/dynamic-data/rules/tenant-costs-one-document.md +22 -0
- package/packs/dynamic-data/skills/design-a-definition/SKILL.md +24 -0
- package/packs/fastify/checks/api-routes-versioned.json +28 -0
- package/packs/fastify/checks/cookie-host-prefix.json +24 -0
- package/packs/fastify/checks/hijack-needs-try.json +28 -0
- package/packs/fastify/checks/no-cast-at-route-boundary.json +24 -0
- package/packs/fastify/checks/no-db-in-route-file.json +28 -0
- package/packs/fastify/checks/no-err-message-to-client.json +24 -0
- package/packs/fastify/checks/no-regexp-from-input.json +24 -0
- package/packs/fastify/checks/no-trust-proxy-true.json +25 -0
- package/packs/fastify/checks/one-error-body.json +26 -0
- package/packs/fastify/checks/origin-from-config-not-host.json +31 -0
- package/packs/fastify/checks/password-needs-slow-hash.json +30 -0
- package/packs/fastify/checks/sec-headers-present.json +28 -0
- package/packs/fastify/checks/secrets-compared-constant-time.json +24 -0
- package/packs/fastify/checks/sse-needs-error-listener.json +28 -0
- package/packs/fastify/checks/user-bytes-need-disposition.json +29 -0
- package/packs/fastify/pack.json +41 -0
- package/packs/fastify/rules/credentials-and-input.md +23 -0
- package/packs/fastify/rules/security-one-switch.md +28 -0
- package/packs/fastify/rules/streams-and-hijacks.md +22 -0
- package/packs/fastify/rules/tests-use-inject.md +13 -0
- package/packs/fastify/rules/three-layers.md +20 -0
- package/packs/fastify/rules/trust-config-not-headers.md +24 -0
- package/packs/full-stack/checks/no-express-no-vite.json +23 -0
- package/packs/full-stack/hooks/stack-guard.mjs +90 -0
- package/packs/full-stack/pack.json +76 -0
- package/packs/full-stack/rules/stack-is-fastify-and-nextjs.md +20 -0
- package/packs/go/checks/go-exec-result-ignored.json +15 -0
- package/packs/go/checks/go-unmarshal-error-ignored.json +15 -0
- package/packs/go/pack.json +21 -0
- package/packs/go/rules/go-errors-are-handled.md +22 -0
- package/packs/gpu-painter/checks/dawn-backends-and-validation.json +9 -0
- package/packs/gpu-painter/checks/dawn-no-spontaneous.json +10 -0
- package/packs/gpu-painter/checks/dawn-present-on-render-thread.json +17 -0
- package/packs/gpu-painter/checks/gpu-no-frame-stalls.json +12 -0
- package/packs/gpu-painter/checks/no-opengl-link.json +11 -0
- package/packs/gpu-painter/checks/no-opengl.json +13 -0
- package/packs/gpu-painter/hooks/browser-guard.py +196 -0
- package/packs/gpu-painter/pack.json +91 -0
- package/packs/gpu-painter/rules/dawn-build-and-shaders.md +27 -0
- package/packs/gpu-painter/rules/dawn-device-and-render-thread.md +26 -0
- package/packs/gpu-painter/rules/gpu-antialiasing.md +20 -0
- package/packs/gpu-painter/rules/gpu-blend-order.md +22 -0
- package/packs/gpu-painter/rules/gpu-bloom.md +20 -0
- package/packs/gpu-painter/rules/gpu-frame-budget.md +25 -0
- package/packs/gpu-painter/rules/gpu-frame-pacing-and-power.md +27 -0
- package/packs/gpu-painter/rules/gpu-frame-work.md +30 -0
- package/packs/gpu-painter/rules/gpu-golden-images.md +21 -0
- package/packs/gpu-painter/rules/gpu-hdr-and-tonemap.md +23 -0
- package/packs/gpu-painter/rules/gpu-picking-on-cpu.md +19 -0
- package/packs/gpu-painter/rules/gpu-render-lifecycle.md +25 -0
- package/packs/gpu-painter/rules/gpu-text-atlas.md +24 -0
- package/packs/gpu-painter/rules/gpu-travellers-from-snapshot.md +24 -0
- package/packs/gpu-painter/rules/hover-every-pressable.md +25 -0
- package/packs/gpu-painter/skills/gpu-capture/SKILL.md +15 -0
- package/packs/gpu-painter/skills/gpu-frame-bench/SKILL.md +14 -0
- package/packs/gpu-painter/skills/gpu-golden-check/SKILL.md +13 -0
- package/packs/gpu-painter/skills/gpu-golden-update/SKILL.md +16 -0
- package/packs/gpu-painter/skills/gpu-lifecycle-stress/SKILL.md +15 -0
- package/packs/gpu-painter/skills/hover-state/SKILL.md +33 -0
- package/packs/hook-proof/checks/hook-catch-must-not-allow.json +16 -0
- package/packs/hook-proof/checks/hook-command-no-pipe.json +16 -0
- package/packs/hook-proof/checks/hook-exit-1-does-not-block.json +17 -0
- package/packs/hook-proof/checks/hook-no-or-true.json +18 -0
- package/packs/hook-proof/checks/hook-refusal-on-stderr.json +17 -0
- package/packs/hook-proof/pack.json +38 -0
- package/packs/hook-proof/rules/a-refusal-is-exit-2.md +28 -0
- package/packs/hook-proof/skills/prove-a-hook/SKILL.md +21 -0
- package/packs/host-agent/checks/file-path-from-param.json +27 -0
- package/packs/host-agent/checks/integrity-check-optional.json +27 -0
- package/packs/host-agent/checks/quoted-value-into-config.json +24 -0
- package/packs/host-agent/checks/secret-on-argv.json +28 -0
- package/packs/host-agent/checks/ssh-verifies-host-key.json +27 -0
- package/packs/host-agent/checks/system-file-written-in-place.json +26 -0
- package/packs/host-agent/checks/tmp-path-predictable.json +27 -0
- package/packs/host-agent/pack.json +22 -0
- package/packs/host-agent/rules/root-on-other-machines.md +27 -0
- package/packs/html-ui/checks/email-html-escapes.json +25 -0
- package/packs/html-ui/checks/external-link-rel.json +21 -0
- package/packs/html-ui/checks/no-duplicate-escape.json +26 -0
- package/packs/html-ui/checks/no-inline-client-script.json +21 -0
- package/packs/html-ui/checks/no-inline-style.json +23 -0
- package/packs/html-ui/pack.json +33 -0
- package/packs/html-ui/rules/email-html-escapes.md +19 -0
- package/packs/html-ui/rules/server-html-escapes-everything.md +22 -0
- package/packs/http-api/checks/api-path-versioned.json +31 -0
- package/packs/http-api/checks/authz-from-request-body.json +26 -0
- package/packs/http-api/checks/client-calls-a-version.json +28 -0
- package/packs/http-api/checks/client-ip-not-leftmost-xff.json +28 -0
- package/packs/http-api/checks/cors-needs-origin.json +26 -0
- package/packs/http-api/checks/csp-not-switched-off.json +28 -0
- package/packs/http-api/checks/debug-route-not-shipped.json +26 -0
- package/packs/http-api/checks/encrypted-name-plaintext-value.json +26 -0
- package/packs/http-api/checks/err-text-stays-server-side.json +27 -0
- package/packs/http-api/checks/failure-is-not-empty.json +28 -0
- package/packs/http-api/checks/gate-is-not-a-todo.json +27 -0
- package/packs/http-api/checks/guard-fails-open.json +28 -0
- package/packs/http-api/checks/hmac-over-reserialised-body.json +26 -0
- package/packs/http-api/checks/jwt-verify-pins-algorithm.json +26 -0
- package/packs/http-api/checks/middleware-skips-without-user.json +26 -0
- package/packs/http-api/checks/next-api-route-versioned.json +47 -0
- package/packs/http-api/checks/no-security-off-switch.json +28 -0
- package/packs/http-api/checks/oauth-state-signed.json +26 -0
- package/packs/http-api/checks/one-time-code-compared-raw.json +26 -0
- package/packs/http-api/checks/query-cast-is-not-validation.json +28 -0
- package/packs/http-api/checks/queue-action-from-client.json +26 -0
- package/packs/http-api/checks/rate-limit-one-module.json +28 -0
- package/packs/http-api/checks/role-checked-in-one-guard.json +27 -0
- package/packs/http-api/checks/route-body-needs-schema.json +31 -0
- package/packs/http-api/checks/route-scopes-to-the-caller.json +31 -0
- package/packs/http-api/checks/secret-compared-constant-time-any.json +28 -0
- package/packs/http-api/checks/secret-in-job-record.json +27 -0
- package/packs/http-api/checks/server-fetches-tenant-url.json +30 -0
- package/packs/http-api/checks/short-code-needs-limit.json +26 -0
- package/packs/http-api/checks/user-url-through-safe-fetch.json +28 -0
- package/packs/http-api/checks/webhook-dedupes-event-id.json +30 -0
- package/packs/http-api/pack.json +45 -0
- package/packs/http-api/rules/adapters-one-db-gateway.md +18 -0
- package/packs/http-api/rules/apis-are-versioned.md +27 -0
- package/packs/http-api/rules/credentials-handled-safely.md +22 -0
- package/packs/http-api/rules/handlers-own-the-work.md +17 -0
- package/packs/http-api/rules/health-and-correlation.md +17 -0
- package/packs/http-api/rules/one-error-contract.md +17 -0
- package/packs/http-api/rules/routes-are-thin.md +17 -0
- package/packs/http-api/rules/routes-scope-to-the-caller.md +26 -0
- package/packs/http-api/rules/routes-validate-and-answer.md +25 -0
- package/packs/http-api/skills/change-an-api/SKILL.md +20 -0
- package/packs/http-api/skills/secure-a-route/SKILL.md +21 -0
- package/packs/instrument-shell/checks/no-raw-struct-blob.json +23 -0
- package/packs/instrument-shell/pack.json +22 -0
- package/packs/instrument-shell/rules/fold-keeps-every-route.md +24 -0
- package/packs/instrument-shell/rules/quiet-announces-itself.md +22 -0
- package/packs/instrument-shell/rules/render-the-extremes.md +25 -0
- package/packs/instrument-shell/rules/seams-are-declared.md +27 -0
- package/packs/instrument-shell/rules/session-blob-append-only.md +25 -0
- package/packs/instrument-shell/skills/gates-from-commit-one/SKILL.md +20 -0
- package/packs/instrument-shell/skills/hit-the-painted-control/SKILL.md +18 -0
- package/packs/js-ts/checks/async-callback-unguarded.json +26 -0
- package/packs/js-ts/checks/background-loop-needs-stop.json +28 -0
- package/packs/js-ts/checks/child-process-error-handler.json +28 -0
- package/packs/js-ts/checks/entry-registers-shutdown.json +31 -0
- package/packs/js-ts/checks/fetch-needs-timeout.json +30 -0
- package/packs/js-ts/checks/job-only-in-memory.json +28 -0
- package/packs/js-ts/checks/json-only-through-helper.json +29 -0
- package/packs/js-ts/checks/line-under-200-chars.json +34 -0
- package/packs/js-ts/checks/localhost-default-in-server.json +29 -0
- package/packs/js-ts/checks/no-any.json +27 -0
- package/packs/js-ts/checks/no-cast-escape-hatches.json +24 -0
- package/packs/js-ts/checks/no-console-in-app.json +31 -0
- package/packs/js-ts/checks/no-dynamic-import-node-builtin.json +25 -0
- package/packs/js-ts/checks/no-inline-binary.json +20 -0
- package/packs/js-ts/checks/no-inline-zod-outside-schemas.json +27 -0
- package/packs/js-ts/checks/no-local-sleep.json +25 -0
- package/packs/js-ts/checks/no-nested-ternary.json +20 -0
- package/packs/js-ts/checks/no-secret-in-log.json +24 -0
- package/packs/js-ts/checks/no-shell-string-interpolation.json +24 -0
- package/packs/js-ts/checks/no-swallowed-errors.json +25 -0
- package/packs/js-ts/checks/no-ts-enum.json +20 -0
- package/packs/js-ts/checks/process-env-only-in-config.json +31 -0
- package/packs/js-ts/checks/race-leaves-work-running.json +23 -0
- package/packs/js-ts/checks/reject-as-catch-all-exists.json +24 -0
- package/packs/js-ts/checks/secret-has-no-fallback.json +24 -0
- package/packs/js-ts/checks/secret-needs-crypto-random.json +24 -0
- package/packs/js-ts/checks/server-close-awaited.json +27 -0
- package/packs/js-ts/checks/session-user-typed-once.json +24 -0
- package/packs/js-ts/checks/switch-is-a-table.json +24 -0
- package/packs/js-ts/checks/test-imports-the-source.json +19 -0
- package/packs/js-ts/checks/test-name-says-behaviour.json +16 -0
- package/packs/js-ts/checks/tsconfig-flags.json +21 -0
- package/packs/js-ts/checks/void-needs-catch.json +24 -0
- package/packs/js-ts/checks/zod-string-needs-max.json +25 -0
- package/packs/js-ts/checks/zod-v4-formats.json +23 -0
- package/packs/js-ts/pack.json +84 -0
- package/packs/js-ts/rules/await-handled-at-a-boundary.md +66 -0
- package/packs/js-ts/rules/env-through-config.md +36 -0
- package/packs/js-ts/rules/errors-never-swallowed.md +33 -0
- package/packs/js-ts/rules/files-under-300-lines.md +18 -0
- package/packs/js-ts/rules/guard-clauses-over-nesting.md +34 -0
- package/packs/js-ts/rules/js-repeats-become-templates.md +26 -0
- package/packs/js-ts/rules/json-only-through-one-helper.md +62 -0
- package/packs/js-ts/rules/named-constants-not-magic-numbers.md +34 -0
- package/packs/js-ts/rules/no-any.md +34 -0
- package/packs/js-ts/rules/no-console.md +30 -0
- package/packs/js-ts/rules/no-floating-promises.md +31 -0
- package/packs/js-ts/rules/no-param-mutation.md +28 -0
- package/packs/js-ts/rules/no-unsafe-assertions.md +30 -0
- package/packs/js-ts/rules/one-logger.md +24 -0
- package/packs/js-ts/rules/outbound-calls-have-a-limit.md +20 -0
- package/packs/js-ts/rules/parallel-independent-awaits.md +29 -0
- package/packs/js-ts/rules/process-lifecycle.md +47 -0
- package/packs/js-ts/rules/schema-single-source-of-truth.md +36 -0
- package/packs/js-ts/rules/secrets-stay-secret.md +22 -0
- package/packs/js-ts/rules/small-functions.md +20 -0
- package/packs/js-ts/rules/test-names-describe-behavior.md +28 -0
- package/packs/js-ts/rules/tests-can-fail.md +19 -0
- package/packs/js-ts/rules/tsconfig-strict.md +32 -0
- package/packs/js-ts/rules/typed-exports.md +27 -0
- package/packs/js-ts/rules/types-once-at-the-edge.md +18 -0
- package/packs/js-ts/rules/unions-over-enums.md +29 -0
- package/packs/js-ts/rules/zod-at-route-boundaries.md +40 -0
- package/packs/midi/checks/active-sensing-no-pulse.json +24 -0
- package/packs/midi/checks/clock-not-at-sample-zero.json +23 -0
- package/packs/midi/checks/midi-realtime-first.json +27 -0
- package/packs/midi/checks/no-tempo-writeback.json +23 -0
- package/packs/midi/checks/position-not-integrated.json +25 -0
- package/packs/midi/checks/stub-playhead-advances.json +27 -0
- package/packs/midi/checks/sysex-length-literal.json +23 -0
- package/packs/midi/checks/sysex-not-on-audio.json +24 -0
- package/packs/midi/checks/sysex-survives-realtime.json +27 -0
- package/packs/midi/pack.json +22 -0
- package/packs/midi/rules/clock-is-a-pulse-stream.md +30 -0
- package/packs/midi/rules/imports-count-every-loss.md +25 -0
- package/packs/midi/rules/measure-the-clock-in-audio.md +30 -0
- package/packs/midi/rules/sync-is-a-mode.md +29 -0
- package/packs/midi/rules/sysex-is-a-record.md +29 -0
- package/packs/midi/skills/clock-probe/SKILL.md +23 -0
- package/packs/midi/skills/clock-probe/clock-law.md +417 -0
- package/packs/midi/skills/does-it-sync/SKILL.md +19 -0
- package/packs/mongo/checks/bulk-over-many.json +28 -0
- package/packs/mongo/checks/created-at-immutable.json +25 -0
- package/packs/mongo/checks/driver-outside-gateway.json +24 -0
- package/packs/mongo/checks/key-lookup-is-hashed.json +24 -0
- package/packs/mongo/checks/mongoclient-cached-and-named.json +34 -0
- package/packs/mongo/checks/mongoclient-outside-gateway.json +24 -0
- package/packs/mongo/checks/no-count-plus-one-ids.json +25 -0
- package/packs/mongo/checks/no-db-write-in-loop.json +25 -0
- package/packs/mongo/checks/no-drop-in-code.json +27 -0
- package/packs/mongo/checks/no-empty-deletemany.json +26 -0
- package/packs/mongo/checks/no-find-reads.json +26 -0
- package/packs/mongo/checks/no-input-spread-into-write.json +24 -0
- package/packs/mongo/checks/no-mongoose.json +23 -0
- package/packs/mongo/checks/no-negation-delete.json +26 -0
- package/packs/mongo/checks/no-non-null-after-findone.json +25 -0
- package/packs/mongo/checks/objectid-equals.json +26 -0
- package/packs/mongo/checks/update-then-reread.json +24 -0
- package/packs/mongo/checks/watch-not-poll.json +25 -0
- package/packs/mongo/pack.json +52 -0
- package/packs/mongo/rules/deletes-name-what-dies.md +21 -0
- package/packs/mongo/rules/driver-only-in-gateway.md +26 -0
- package/packs/mongo/rules/keys-hashed-input-picked.md +21 -0
- package/packs/mongo/rules/one-client-one-pool.md +32 -0
- package/packs/mongo/rules/reads-aggregate-writes-bulk.md +25 -0
- package/packs/mongo/rules/writes-are-atomic.md +27 -0
- package/packs/multi-tenant/checks/role-sees-all-tenants.json +26 -0
- package/packs/multi-tenant/checks/tenant-filter-from-url.json +25 -0
- package/packs/multi-tenant/pack.json +22 -0
- package/packs/multi-tenant/rules/no-role-crosses-a-tenant.md +21 -0
- package/packs/multi-tenant/skills/prove-tenant-isolation/SKILL.md +19 -0
- package/packs/nginx/checks/modsec-no-blanket-off.json +16 -0
- package/packs/nginx/checks/nginx-csp-no-unsafe.json +15 -0
- package/packs/nginx/checks/nginx-headers-always.json +15 -0
- package/packs/nginx/checks/nginx-keepalive-http11.json +20 -0
- package/packs/nginx/checks/nginx-logs-to-stdout.json +15 -0
- package/packs/nginx/checks/nginx-no-crlf.json +17 -0
- package/packs/nginx/checks/nginx-resolver-ttl.json +19 -0
- package/packs/nginx/pack.json +43 -0
- package/packs/nginx/rules/modsecurity-tuning.md +22 -0
- package/packs/nginx/rules/nginx-production.md +26 -0
- package/packs/nginx/skills/waf-rollout/SKILL.md +20 -0
- package/packs/python/checks/py-no-bare-except.json +18 -0
- package/packs/python/checks/py-no-eval.json +18 -0
- package/packs/python/checks/py-no-mutable-default.json +18 -0
- package/packs/python/checks/py-no-shell-true.json +18 -0
- package/packs/python/checks/py-no-star-import.json +18 -0
- package/packs/python/checks/py-no-swallowed-exception.json +18 -0
- package/packs/python/checks/py-none-by-is.json +18 -0
- package/packs/python/checks/py-open-with-with.json +18 -0
- package/packs/python/checks/py-requests-timeout.json +18 -0
- package/packs/python/checks/py-subprocess-output-checked.json +18 -0
- package/packs/python/checks/py-yaml-safe-load.json +18 -0
- package/packs/python/pack.json +26 -0
- package/packs/python/rules/python-errors-and-resources.md +26 -0
- package/packs/python/rules/python-repeats-become-templates.md +27 -0
- package/packs/python/skills/python-extract-template/SKILL.md +35 -0
- package/packs/react/checks/dialog-needs-role.json +28 -0
- package/packs/react/checks/effect-not-for-derived-state.json +24 -0
- package/packs/react/checks/icon-button-needs-name.json +24 -0
- package/packs/react/checks/label-needs-a-field.json +24 -0
- package/packs/react/checks/ui-failed-fetch-says-so.json +23 -0
- package/packs/react/checks/ui-ok-branch-has-an-else.json +23 -0
- package/packs/react/pack.json +20 -0
- package/packs/react/rules/react-accessible-controls.md +20 -0
- package/packs/react/rules/react-state-and-effects.md +23 -0
- package/packs/reverse-engineering/checks/ear-constant-tagged.json +23 -0
- package/packs/reverse-engineering/checks/no-default-on-distrust.json +24 -0
- package/packs/reverse-engineering/checks/no-rom-table-literal.json +29 -0
- package/packs/reverse-engineering/checks/prose-never-decides.json +22 -0
- package/packs/reverse-engineering/hooks/firmware-out-of-git.mjs +49 -0
- package/packs/reverse-engineering/pack.json +35 -0
- package/packs/reverse-engineering/rules/a-negative-names-what-was-scanned.md +19 -0
- package/packs/reverse-engineering/rules/account-every-term.md +20 -0
- package/packs/reverse-engineering/rules/addresses-name-their-space.md +21 -0
- package/packs/reverse-engineering/rules/every-law-has-a-source.md +18 -0
- package/packs/reverse-engineering/rules/findings-are-withdrawn-in-place.md +17 -0
- package/packs/reverse-engineering/rules/firmware-stays-out.md +18 -0
- package/packs/reverse-engineering/rules/names-before-code.md +19 -0
- package/packs/reverse-engineering/rules/reproduction-or-original.md +18 -0
- package/packs/reverse-engineering/rules/two-sources-before-naming.md +21 -0
- package/packs/reverse-engineering/skills/bank-census/SKILL.md +18 -0
- package/packs/reverse-engineering/skills/gather-sources/SKILL.md +21 -0
- package/packs/reverse-engineering/skills/menu-map/SKILL.md +28 -0
- package/packs/reverse-engineering/skills/names-first/SKILL.md +41 -0
- package/packs/reverse-engineering/skills/re-deliverables/SKILL.md +21 -0
- package/packs/reverse-engineering/skills/tripwire-the-law/SKILL.md +19 -0
- package/packs/reverse-engineering/tools/lib/unwrap.mjs +115 -0
- package/packs/reverse-engineering/tools/names-sweep.mjs +264 -0
- package/packs/shell/checks/sh-mktemp.json +18 -0
- package/packs/shell/checks/sh-no-backticks.json +18 -0
- package/packs/shell/checks/sh-no-eval.json +18 -0
- package/packs/shell/checks/sh-no-parse-ls.json +18 -0
- package/packs/shell/checks/sh-no-pipe-to-shell.json +18 -0
- package/packs/shell/checks/sh-no-program-in-heredoc.json +18 -0
- package/packs/shell/checks/sh-quote-arguments.json +18 -0
- package/packs/shell/checks/sh-rm-guarded.json +18 -0
- package/packs/shell/checks/sh-strict-mode.json +20 -0
- package/packs/shell/pack.json +28 -0
- package/packs/shell/rules/shell-repeats-become-templates.md +23 -0
- package/packs/shell/rules/shell-strict-and-quoted.md +27 -0
- package/packs/shell/skills/shell-extract-template/SKILL.md +32 -0
- package/packs/shell-hooks/checks/gnu-only-in-shell.json +18 -0
- package/packs/shell-hooks/checks/hook-command-anchored.json +12 -0
- package/packs/shell-hooks/checks/hook-json-by-tool.json +18 -0
- package/packs/shell-hooks/checks/no-bash-c-with-variable.json +20 -0
- package/packs/shell-hooks/pack.json +36 -0
- package/packs/shell-hooks/rules/hooks-portable-anchored-quoted.md +26 -0
- package/packs/sound-design/checks/audio-seed-fixed.json +23 -0
- package/packs/sound-design/checks/mix-is-a-crossfade.json +30 -0
- package/packs/sound-design/checks/no-hash-in-the-signal.json +30 -0
- package/packs/sound-design/checks/no-literal-key-anchor.json +30 -0
- package/packs/sound-design/checks/test-compares-a-function-with-itself.json +22 -0
- package/packs/sound-design/pack.json +22 -0
- package/packs/sound-design/rules/algorithms-are-data.md +26 -0
- package/packs/sound-design/rules/meter-before-engine.md +27 -0
- package/packs/sound-design/rules/neutral-is-exact.md +26 -0
- package/packs/sound-design/rules/test-off-the-easy-point.md +28 -0
- package/packs/sound-design/rules/unreachable-is-said.md +25 -0
- package/packs/sound-design/rules/waveforms-and-octaves.md +24 -0
- package/packs/sound-design/skills/choose-observable/SKILL.md +18 -0
- package/packs/sound-design/skills/measure-a-law/SKILL.md +18 -0
- package/packs/sound-design/skills/measure-reverb/SKILL.md +19 -0
- package/packs/sound-design/skills/spread-probe/SKILL.md +18 -0
- package/packs/stellix/checks/no-keyboard-focus.json +12 -0
- package/packs/stellix/checks/one-seeded-random.json +16 -0
- package/packs/stellix/checks/world-never-includes-painter.json +12 -0
- package/packs/stellix/checks/world-no-libm.json +15 -0
- package/packs/stellix/checks/world-no-unordered.json +10 -0
- package/packs/stellix/hooks/no-shipped-images.mjs +88 -0
- package/packs/stellix/pack.json +61 -0
- package/packs/stellix/rules/stellix-deterministic-world.md +29 -0
- package/packs/stellix/rules/stellix-reset-generated.md +24 -0
- package/packs/stellix/rules/stellix-world-snapshot.md +27 -0
- package/packs/stellix/rules/world-continuous-collision.md +26 -0
- package/packs/stellix/rules/world-fp-determinism.md +28 -0
- package/packs/stored-text/checks/no-raw-html-from-data.json +31 -0
- package/packs/stored-text/checks/stored-text-libs-only-in-renderer.json +31 -0
- package/packs/stored-text/checks/stored-text-renderer-complete.json +24 -0
- package/packs/stored-text/checks/stored-text-through-renderer.json +25 -0
- package/packs/stored-text/pack.json +41 -0
- package/packs/stored-text/rules/stored-text-renders-once.md +26 -0
- package/packs/stored-text/skills/render-stored-text/SKILL.md +24 -0
- package/packs/synth-creator/checks/timing-from-synthcore.json +24 -0
- package/packs/synth-creator/checks/ui-drawn-in-code.json +23 -0
- package/packs/synth-creator/hooks/build-guard.mjs +232 -0
- package/packs/synth-creator/hooks/build-sweep.mjs +166 -0
- package/packs/synth-creator/hooks/frozen-files.mjs +63 -0
- package/packs/synth-creator/hooks/idle-wake.mjs +90 -0
- package/packs/synth-creator/hooks/protect-vendor.mjs +174 -0
- package/packs/synth-creator/hooks/spec-lock.mjs +117 -0
- package/packs/synth-creator/hooks/stage-guard.mjs +50 -0
- package/packs/synth-creator/hooks/time-guard.mjs +292 -0
- package/packs/synth-creator/hooks/user-asks.mjs +165 -0
- package/packs/synth-creator/pack.json +136 -0
- package/packs/synth-creator/rules/copy-trees-read-only.md +25 -0
- package/packs/synth-creator/rules/debug-by-measurement.md +20 -0
- package/packs/synth-creator/rules/done-means-fresh-evidence.md +18 -0
- package/packs/synth-creator/rules/hooks-and-gates.md +21 -0
- package/packs/synth-creator/rules/long-work-in-the-background.md +16 -0
- package/packs/synth-creator/rules/timing-from-synthcore.md +16 -0
- package/packs/synth-creator/skills/build-step/SKILL.md +125 -0
- package/packs/synth-creator/skills/build-through-the-queue/SKILL.md +19 -0
- package/packs/synth-creator/tools/freeze.mjs +36 -0
- package/packs/tui/checks/tui-entry-restores-terminal.json +27 -0
- package/packs/tui/checks/tui-gates-on-tty.json +25 -0
- package/packs/tui/checks/tui-no-ris-reset.json +22 -0
- package/packs/tui/checks/tui-resize-listener-removed.json +26 -0
- package/packs/tui/checks/tui-width-is-state.json +33 -0
- package/packs/tui/pack.json +44 -0
- package/packs/tui/rules/tui-resize-and-screen.md +25 -0
- package/packs/tui/skills/build-tui/SKILL.md +21 -0
- package/packs/ui-craft/checks/ui-input-needs-autocomplete.json +23 -0
- package/packs/ui-craft/checks/ui-intl-for-dates.json +21 -0
- package/packs/ui-craft/checks/ui-no-ai-palette.json +22 -0
- package/packs/ui-craft/checks/ui-no-animate-layout.json +22 -0
- package/packs/ui-craft/checks/ui-no-assistant-palette.json +23 -0
- package/packs/ui-craft/checks/ui-no-bounce-easing.json +22 -0
- package/packs/ui-craft/checks/ui-no-custom-cursor.json +22 -0
- package/packs/ui-craft/checks/ui-no-emoji-as-ui.json +22 -0
- package/packs/ui-craft/checks/ui-no-empty-image.json +21 -0
- package/packs/ui-craft/checks/ui-no-eyebrow.json +21 -0
- package/packs/ui-craft/checks/ui-no-furniture-text.json +22 -0
- package/packs/ui-craft/checks/ui-no-gradient-text.json +22 -0
- package/packs/ui-craft/checks/ui-no-index-numbering.json +21 -0
- package/packs/ui-craft/checks/ui-no-lorem.json +22 -0
- package/packs/ui-craft/checks/ui-no-marketing-buzzword.json +22 -0
- package/packs/ui-craft/checks/ui-no-paste-block.json +21 -0
- package/packs/ui-craft/checks/ui-no-placeholder-names.json +21 -0
- package/packs/ui-craft/checks/ui-no-scroll-hijack.json +21 -0
- package/packs/ui-craft/checks/ui-no-side-tab.json +21 -0
- package/packs/ui-craft/checks/ui-no-stock-labels.json +22 -0
- package/packs/ui-craft/checks/ui-no-transition-all.json +22 -0
- package/packs/ui-craft/checks/ui-no-uppercase-table-header.json +21 -0
- package/packs/ui-craft/checks/ui-no-zebra-rows.json +22 -0
- package/packs/ui-craft/checks/ui-no-zoom-lock.json +21 -0
- package/packs/ui-craft/pack.json +43 -0
- package/packs/ui-craft/rules/apps-are-not-pages.md +26 -0
- package/packs/ui-craft/rules/data-tells-the-truth.md +26 -0
- package/packs/ui-craft/rules/dates-and-numbers.md +18 -0
- package/packs/ui-craft/rules/forms-never-fight.md +22 -0
- package/packs/ui-craft/rules/interface-copy.md +26 -0
- package/packs/ui-craft/rules/motion-is-rare.md +31 -0
- package/packs/ui-craft/rules/no-generated-look.md +37 -0
- package/packs/ui-craft/rules/page-holds-its-frame.md +28 -0
- package/packs/ui-craft/rules/page-proved-rendered.md +19 -0
- package/packs/ui-craft/rules/pictures-do-a-job.md +25 -0
- package/packs/ui-craft/rules/pointer-feedback.md +28 -0
- package/packs/ui-craft/rules/screen-answers-one-question.md +24 -0
- package/packs/ui-craft/rules/survives-real-data.md +23 -0
- package/packs/ui-craft/rules/touch-and-small-screens.md +19 -0
- package/packs/ui-craft/rules/type-space-colour.md +26 -0
- package/packs/ui-craft/rules/url-holds-the-state.md +19 -0
- package/packs/ui-craft/skills/app-screen/SKILL.md +33 -0
- package/packs/ui-craft/skills/build-from-reference/SKILL.md +28 -0
- package/packs/ui-craft/skills/chart-choice/SKILL.md +34 -0
- package/packs/ui-craft/skills/hover-state/SKILL.md +28 -0
- package/packs/ui-craft/skills/page-critique/SKILL.md +33 -0
- package/packs/ui-craft/skills/prove-page/SKILL.md +23 -0
- package/packs/ui-craft/skills/redesign-page/SKILL.md +19 -0
- package/packs/ui-craft/skills/ui-code-review/SKILL.md +22 -0
- package/packs/ui-craft/skills/write-the-page/SKILL.md +21 -0
- package/packs/ui-craft/tools/page-measure.mjs +41 -0
- package/packs/web/checks/a11y-img-needs-alt.json +26 -0
- package/packs/web/checks/a11y-no-bare-outline-none.json +21 -0
- package/packs/web/checks/a11y-no-clickable-div.json +26 -0
- package/packs/web/checks/a11y-no-positive-tabindex.json +26 -0
- package/packs/web/checks/client-no-api-key.json +29 -0
- package/packs/web/checks/client-no-browser-global-at-module-scope.json +28 -0
- package/packs/web/checks/client-no-server-import.json +31 -0
- package/packs/web/checks/client-no-window-location-nav.json +28 -0
- package/packs/web/checks/css-no-inline-style.json +30 -0
- package/packs/web/checks/ds-pages-compose-primitives.json +28 -0
- package/packs/web/checks/key-from-index.json +23 -0
- package/packs/web/checks/prodbuild-no-cdn-runtime-deps.json +21 -0
- package/packs/web/checks/prodbuild-no-dev-flags.json +21 -0
- package/packs/web/checks/prodbuild-no-remote-fonts.json +22 -0
- package/packs/web/checks/rcss-button-cursor.json +30 -0
- package/packs/web/checks/rcss-no-100vw.json +21 -0
- package/packs/web/checks/rcss-no-fixed-wide-width.json +21 -0
- package/packs/web/checks/rcss-viewport-meta.json +28 -0
- package/packs/web/checks/secret-not-in-web-storage.json +25 -0
- package/packs/web/checks/tw-no-raw-hex-in-markup.json +34 -0
- package/packs/web/checks/wp-hero-not-lazy.json +27 -0
- package/packs/web/checks/wp-img-needs-dimensions.json +26 -0
- package/packs/web/pack.json +73 -0
- package/packs/web/rules/accessibility.md +26 -0
- package/packs/web/rules/client-boundaries.md +23 -0
- package/packs/web/rules/production-build.md +22 -0
- package/packs/web/rules/secrets-not-in-the-browser.md +18 -0
- package/packs/web/rules/styles-live-in-stylesheets.md +23 -0
- package/packs/web/rules/tailwind-v4.md +23 -0
- package/packs/web/rules/ui-primitives-one-module.md +30 -0
- package/packs/web/rules/web-performance.md +21 -0
- package/packs/web/skills/design-direction/SKILL.md +30 -0
- package/packs/web/skills/design-review/SKILL.md +28 -0
- package/packs/web/skills/pick-rendering-mode/SKILL.md +21 -0
- package/packs/web/skills/preflight-build/SKILL.md +23 -0
- package/packs/web/skills/seo-audit/SKILL.md +22 -0
- package/packs/websocket/checks/no-token-in-query.json +26 -0
- package/packs/websocket/checks/stale-socket-deletes-new-entry.json +25 -0
- package/packs/websocket/checks/ws-identity-from-message.json +26 -0
- package/packs/websocket/pack.json +21 -0
- package/packs/websocket/rules/identity-comes-from-the-connection.md +24 -0
- package/packs/writing/checks/ascii-no-em-dash.json +17 -0
- package/packs/writing/checks/no-copula-dodging.json +18 -0
- package/packs/writing/checks/no-dramatic-colon.json +18 -0
- package/packs/writing/checks/no-not-x-but-y.json +18 -0
- package/packs/writing/checks/no-participial-opener.json +18 -0
- package/packs/writing/checks/no-pseudo-wisdom.json +18 -0
- package/packs/writing/checks/no-slop-words.json +18 -0
- package/packs/writing/checks/no-stock-connectors.json +18 -0
- package/packs/writing/checks/no-time-or-date.json +27 -0
- package/packs/writing/checks/no-trailing-ing.json +18 -0
- package/packs/writing/checks/no-vague-attribution.json +18 -0
- package/packs/writing/pack.json +43 -0
- package/packs/writing/rules/ascii-no-em-dash.md +21 -0
- package/packs/writing/rules/cut-the-padding.md +20 -0
- package/packs/writing/rules/direct-voice.md +22 -0
- package/packs/writing/rules/no-cargo-cult.md +16 -0
- package/packs/writing/rules/no-scaffolding.md +19 -0
- package/packs/writing/rules/no-stock-language.md +27 -0
- package/packs/writing/rules/no-time-or-date.md +18 -0
- package/packs/writing/rules/rhythm-like-talk.md +20 -0
- package/packs/writing/rules/write-from-your-own-corner.md +20 -0
- package/packs/writing/skills/argument-post/SKILL.md +28 -0
- package/packs/writing/skills/humanize/SKILL.md +28 -0
- package/packs/writing/skills/launch-post/SKILL.md +38 -0
- package/packs/writing/skills/pick-subreddit/SKILL.md +69 -0
- package/packs/writing/skills/post-voice/SKILL.md +22 -0
- package/packs/writing/skills/reddit-post/SKILL.md +63 -0
- package/packs/writing/skills/reddit-title/SKILL.md +23 -0
- package/packs/writing/skills/reply-moves/SKILL.md +40 -0
- package/packs/writing/skills/reply-stance/SKILL.md +23 -0
- package/packs/writing/skills/should-i-reply/SKILL.md +21 -0
- package/packs/zod/hooks/schemas-only.mjs +53 -0
- package/packs/zod/pack.json +44 -0
- package/packs/zod/rules/env-shapes-only.md +14 -0
- package/packs/zod/rules/zod-4-schemas.md +14 -0
- package/profiles/stellix.layout.json +315 -0
- package/profiles/sub37.layout.json +247 -0
- package/prompts/scan-review.md +40 -0
- package/prompts/survey.md +82 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "stub-reports-success",
|
|
3
|
+
"why": "A control that reports success and does nothing is worse than none, because the team stops looking.",
|
|
4
|
+
"rule": ".claude/rules/a-control-is-seen-to-fire.md",
|
|
5
|
+
"globs": [
|
|
6
|
+
"**/*.ts",
|
|
7
|
+
"**/*.tsx",
|
|
8
|
+
"**/*.js",
|
|
9
|
+
"**/*.mjs",
|
|
10
|
+
"**/*.cjs",
|
|
11
|
+
"**/*.py",
|
|
12
|
+
"**/*.go",
|
|
13
|
+
"**/*.cpp",
|
|
14
|
+
"**/*.h"
|
|
15
|
+
],
|
|
16
|
+
"exclude": [
|
|
17
|
+
"**/tests/**",
|
|
18
|
+
"**/test/**",
|
|
19
|
+
"**/*.test.*",
|
|
20
|
+
"**/*.spec.*",
|
|
21
|
+
"**/e2e/**",
|
|
22
|
+
"**/node_modules/**",
|
|
23
|
+
"**/dist/**",
|
|
24
|
+
"**/build/**",
|
|
25
|
+
"**/.next/**"
|
|
26
|
+
],
|
|
27
|
+
"pattern": "(?://|\\*)\\s*(?:for now|stub|placeholder|would (?:decrypt|connect|send|call))[^\\n]*(?:\\n[^\\n]*){0,2}?\\b(?:success:\\s*true|return\\s+true|connected:\\s*true|ok:\\s*true)",
|
|
28
|
+
"bad": "// For now, return success\nres.json({ success: true, data: { connected: true } });",
|
|
29
|
+
"good": "await probe(uri);\nres.json({ success: true, data: { connected: true } });",
|
|
30
|
+
"flags": "i"
|
|
31
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "test-skip-on-failed-setup",
|
|
3
|
+
"why": "A skip that hides a failed setup reports green on a broken product.",
|
|
4
|
+
"rule": ".claude/rules/a-control-is-seen-to-fire.md",
|
|
5
|
+
"globs": [
|
|
6
|
+
"**/tests/**/*.ts",
|
|
7
|
+
"**/test/**/*.ts",
|
|
8
|
+
"**/e2e/**/*.ts",
|
|
9
|
+
"**/*.spec.ts"
|
|
10
|
+
],
|
|
11
|
+
"exclude": [
|
|
12
|
+
"**/node_modules/**"
|
|
13
|
+
],
|
|
14
|
+
"pattern": "test\\.skip\\(\\s*!(?![A-Z_]+\\b)\\w+",
|
|
15
|
+
"bad": "test.skip(!wsId, 'No WS created');",
|
|
16
|
+
"good": "expect(wsId).toBeTruthy();"
|
|
17
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PreToolUse (Agent|Task, and Write|Edit|MultiEdit|NotebookEdit|Bash): a brief, a spec or a recipe names an instance of a
|
|
3
|
+
// concept only as the example. KWB-1: one spec sentence ("PIANO in slot 1 and PAD in slot 2") and the recipes' "Slot 1 /
|
|
4
|
+
// Slot 2" headings went into nine agent briefs, and every brief got work built and proved on slot 2 alone.
|
|
5
|
+
// Judged: an agent's prompt, and what a write ADDS to docs/** or inbox/** (by every write route, lib/writes.mjs). An
|
|
6
|
+
// instance of a concept in design/shape.json (lib/shape.mjs: a number, an ordinal, a role name before a concept word, a
|
|
7
|
+
// pattern, an array index) is allowed only on a "seen in:" or "example:" line, or as a declared constant read by name (the
|
|
8
|
+
// number rule's third form; KWB-4); a selector does not excuse
|
|
9
|
+
// it here (a brief says "initSlot", not "slot 1"). The refusal quotes the manifest row and the fix: name the shape and
|
|
10
|
+
// the generator; the instance is the example. Every refusal is counted per concept in .claude/usage.log. `--selftest`.
|
|
11
|
+
import { readFileSync } from 'node:fs';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { checkoutRoot, insideProject, relInCheckout, ROOT } from './lib/project.mjs';
|
|
15
|
+
import { addedFindings, ASK, findInstanceLiterals, isExcluded, logRefusal, quoteRow, readShape } from './lib/shape.mjs';
|
|
16
|
+
import { bashOf, runHook, scratchProject, verdicts, writeOf } from './lib/selftest.mjs';
|
|
17
|
+
import { writesOf } from './lib/writes.mjs';
|
|
18
|
+
import { asHook, isMain } from './lib/hook-log.mjs';
|
|
19
|
+
|
|
20
|
+
const BRIEFS = /^(docs|inbox)\//;
|
|
21
|
+
const find = (shape) => (t) => findInstanceLiterals(t, 'brief.md', shape, { only: 'brief' });
|
|
22
|
+
|
|
23
|
+
function refuse(where, findings, shape) {
|
|
24
|
+
const lines = findings.slice(0, 8).map((f) => ` ${where}:${f.line}: "${f.text.length > 140 ? `${f.text.slice(0, 137)}...` : f.text}" names ${f.concept} instance ${f.instance} (${f.why})`);
|
|
25
|
+
const rows = [...new Set(findings.map((f) => f.concept))].map((c) => {
|
|
26
|
+
const k = shape.concepts[c];
|
|
27
|
+
const how = k.shape === 'many' ? `many${k.count ? `(${k.count})` : ''}, made by ${k.row.generator}` : `one, id ${k.id}`;
|
|
28
|
+
return ` ${quoteRow(c, shape)}\n fix: name the shape and the generator (every ${c}: ${how}); the instance is the example, on its own "seen in:" or "example:" line`;
|
|
29
|
+
});
|
|
30
|
+
logRefusal(ROOT, 'brief-guard', findings.map((f) => f.concept));
|
|
31
|
+
process.stderr.write(`BLOCKED by brief-guard: ${where} names one instance of a concept outside a "seen in:" or "example:" line — ${ASK}\n${lines.join('\n')}${findings.length > 8 ? `\n ... and ${findings.length - 8} more` : ''}\n${rows.join('\n')}\n`);
|
|
32
|
+
return 2;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function main(text) {
|
|
36
|
+
let payload;
|
|
37
|
+
try { payload = JSON.parse(text); } catch { return 0; }
|
|
38
|
+
const tool = payload.tool_name;
|
|
39
|
+
if (tool === 'Agent' || tool === 'Task') {
|
|
40
|
+
const shape = readShape(ROOT);
|
|
41
|
+
if (!shape || shape.error) return 0;
|
|
42
|
+
const text = [payload.tool_input?.description, payload.tool_input?.prompt].filter((t) => typeof t === 'string').join('\n');
|
|
43
|
+
const hits = find(shape)(text);
|
|
44
|
+
return hits.length ? refuse('the agent brief', hits, shape) : 0;
|
|
45
|
+
}
|
|
46
|
+
for (const w of writesOf(payload, payload.cwd ?? process.cwd())) {
|
|
47
|
+
if (w.after === null || w.op === 'delete' || !insideProject(w.path)) continue;
|
|
48
|
+
const rel = relInCheckout(w.path);
|
|
49
|
+
if (!BRIEFS.test(rel)) continue;
|
|
50
|
+
const shape = readShape(checkoutRoot(w.path)) ?? readShape(ROOT);
|
|
51
|
+
if (!shape || shape.error || isExcluded(rel, shape)) continue;
|
|
52
|
+
const hits = addedFindings(w.before, w.after, find(shape));
|
|
53
|
+
if (hits.length) return refuse(rel, hits, shape);
|
|
54
|
+
}
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const SHAPE = {
|
|
59
|
+
concepts: {
|
|
60
|
+
slot: { shape: 'many', count: 4, generator: 'slotId(i, field)', words: ['slot', 'slots'], selectors: { selectedSlot: 'the slot the player has open' }, constants: { initSlot: { instance: 1, reason: 'INIT plays one saw in slot 1' } } },
|
|
61
|
+
layer: { shape: 'many', generator: 'layerId(i)', words: ['layer', 'layers'] },
|
|
62
|
+
},
|
|
63
|
+
exclude: [],
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
function selftest() {
|
|
67
|
+
const hook = fileURLToPath(import.meta.url);
|
|
68
|
+
const dir = scratchProject({ 'design/shape.json': JSON.stringify(SHAPE), 'docs/recipes.md': '# Recipes\nPLACE\n' });
|
|
69
|
+
const bare = scratchProject({});
|
|
70
|
+
const agent = (prompt, at = dir) => runHook(hook, at, { tool_name: 'Agent', tool_input: { description: 'build', prompt } });
|
|
71
|
+
const doc = (rel, text) => runHook(hook, dir, writeOf(join(dir, rel), text));
|
|
72
|
+
const n = () => { try { return readFileSync(join(dir, '.claude/usage.log'), 'utf8').split('\n').filter((l) => /\tbrief-guard\tslot$/.test(l)).length; } catch { return 0; } };
|
|
73
|
+
const cases = [
|
|
74
|
+
['agent: "drive slot 2"', agent('Build the delay. Drive slot 2 and check it works.'), 2],
|
|
75
|
+
['agent: "the PAD slot"', agent('Give the PAD slot its reverb.'), 2],
|
|
76
|
+
['agent: "the second slot"', agent('Wire the second slot.'), 2],
|
|
77
|
+
['agent: a declared constant read by name passes (the number rule\'s third form)', agent('Start in slot 1 (initSlot).'), 0],
|
|
78
|
+
['doc: the INIT line naming its declared constant passes', doc('docs/recipes.md', '**INIT** (the declared constant initSlot): one saw in slot 1.\n'), 0],
|
|
79
|
+
['agent: a selector is still no excuse in a brief', agent('Use selectedSlot: drive slot 2.'), 2],
|
|
80
|
+
['agent: "seen in: slot 2, build for all slots"', agent('Fix the missing delay.\nseen in: slot 2, build for all slots'), 0],
|
|
81
|
+
['agent: quotes the manifest row (its reason names slot 1)', agent(`The slot row: INIT plays one saw in slot 1. Build for every slot with slotId(i, field).`), 0],
|
|
82
|
+
['agent: names the shape and the generator', agent('Every slot (many(4), slotId(i, field)) gets the delay; loop over all slots.'), 0],
|
|
83
|
+
['agent: no design/shape.json', agent('Drive slot 2.', bare), 0],
|
|
84
|
+
['recipe heading "Slot 1, the lead"', doc('docs/recipes.md', '# Recipes\n## Slot 1, the lead\n'), 2],
|
|
85
|
+
['recipe heading "Layer lead"', doc('docs/recipes.md', '# Recipes\n## Layer lead\n'), 0],
|
|
86
|
+
['inbox brief "drive slot 2"', doc('inbox/brief-7.md', 'Drive slot 2 until it works.\n'), 2],
|
|
87
|
+
['inbox brief "example: slot 2"', doc('inbox/brief-7.md', 'Build the swap for all slots.\nexample: slot 2 lost its delay\n'), 0],
|
|
88
|
+
['bash heredoc into docs/', runHook(hook, dir, bashOf("cat >> docs/recipes.md <<'EOF'\n## Slot 2, the pad\nEOF", dir)), 2],
|
|
89
|
+
['not a brief: src/ is shape-law\'s', doc('src/world/x.md', 'slot 2\n'), 0],
|
|
90
|
+
];
|
|
91
|
+
cases.push(['usage.log counts each refusal per concept', n(), 7]);
|
|
92
|
+
return verdicts('brief-guard', cases);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const hook = main; // run-hooks.mjs calls it with the event's text (KWB-24)
|
|
96
|
+
if (isMain(import.meta.url)) process.exit(process.argv.includes('--selftest') ? selftest() : await asHook('brief-guard', main));
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PreToolUse (Write|Edit|MultiEdit|NotebookEdit|Bash): code stays small in every direction, not only in lines per file. Measured on a real repo: 21% of
|
|
3
|
+
// code lines held two or more statements, so a file "under 300 lines" was 500, and one function held 140 statements in 70 lines. A write is refused when it adds
|
|
4
|
+
// a line too wide or packed, a function too long, or a function with too many parameters. What already stands is judged only if it gets worse.
|
|
5
|
+
// `layout.json` "guards": { "codeShape": { "maxLineChars", "maxStatementsPerLine", "maxFunctionStatements", "maxParameters" } } changes a limit.
|
|
6
|
+
// `--selftest` proves it.
|
|
7
|
+
import { extname } from 'node:path';
|
|
8
|
+
import { statementsOf } from './lib/code-shapes.mjs';
|
|
9
|
+
import { isContent } from './lib/content.mjs';
|
|
10
|
+
import { TESTS } from './lib/elements.mjs';
|
|
11
|
+
import { functionsOf } from './lib/function-shapes.mjs';
|
|
12
|
+
import { matchesAny } from './lib/glob.mjs';
|
|
13
|
+
import { languageOf } from './lib/languages.mjs';
|
|
14
|
+
import { readLayout } from './lib/layout.mjs';
|
|
15
|
+
import { claudeRoot } from './lib/project.mjs';
|
|
16
|
+
import { oneOffLines } from './lib/repo-index.mjs';
|
|
17
|
+
import { bashOf, editOf, runHook, scratchProject, verdicts, writeOf } from './lib/selftest.mjs';
|
|
18
|
+
import { judgeEvent } from './lib/writes.mjs';
|
|
19
|
+
import { asHook, isMain } from './lib/hook-log.mjs';
|
|
20
|
+
|
|
21
|
+
const LIMITS = { maxLineChars: 120, maxStatementsPerLine: 2, maxFunctionStatements: 50, maxParameters: 6 };
|
|
22
|
+
const GENERATED = /\b(?:auto-?generated|generated by|do not edit|@generated)\b/i;
|
|
23
|
+
|
|
24
|
+
// Lines this write adds, by number: a line that was already there is not its fault.
|
|
25
|
+
function addedLines(before, after) {
|
|
26
|
+
const left = before.split('\n').reduce((m, l) => m.set(l, (m.get(l) ?? 0) + 1), new Map());
|
|
27
|
+
return after.split('\n').flatMap((l, i) => { const n = left.get(l) ?? 0; left.set(l, n - 1); return n > 0 ? [] : [i + 1]; });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function lineFault(lang, text, added, off, limits) {
|
|
31
|
+
const code = lang.blank(text).split('\n');
|
|
32
|
+
const perLine = statementsOf(text, lang).reduce((m, s) => m.set(s.line, (m.get(s.line) ?? 0) + 1), new Map());
|
|
33
|
+
for (const n of added.filter((x) => !off.has(x))) {
|
|
34
|
+
const width = code[n - 1]?.trimEnd().length ?? 0;
|
|
35
|
+
if (width > limits.maxLineChars) return `line ${n} is ${width} columns of code (limit ${limits.maxLineChars}). Break it where it reads.`;
|
|
36
|
+
if ((perLine.get(n) ?? 0) > limits.maxStatementsPerLine) return `line ${n} holds ${perLine.get(n)} statements. One statement a line: a diff, a breakpoint and a coverage line then mean something, and the file's size is true.`;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function functionFault(before, after, off, limits) {
|
|
42
|
+
const was = new Map(before.map((f) => [f.name, f]));
|
|
43
|
+
for (const f of after.filter((x) => !off.has(x.line))) {
|
|
44
|
+
const old = was.get(f.name);
|
|
45
|
+
if (f.statements > limits.maxFunctionStatements && f.statements > (old?.statements ?? 0)) return `${f.name} (line ${f.line}) holds ${f.statements} statements (limit ${limits.maxFunctionStatements}). A function that does many things cannot be tested in parts: split it by what it does.`;
|
|
46
|
+
if (f.params > limits.maxParameters && f.params > (old?.params ?? 0)) return `${f.name} (line ${f.line}) takes ${f.params} parameters (limit ${limits.maxParameters}). Pass one struct or object: positional arguments of one type get swapped and still compile.`;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function judgeWrite(w, rel) {
|
|
52
|
+
const ext = extname(w.path);
|
|
53
|
+
const lang = languageOf(ext);
|
|
54
|
+
if (!lang || w.op === 'delete' || w.after === null || w.after === undefined || matchesAny(rel, TESTS) || GENERATED.test(w.after.slice(0, 400))) return null;
|
|
55
|
+
const layout = readLayout(claudeRoot(w.path));
|
|
56
|
+
const limits = { ...LIMITS, ...(layout.guards?.codeShape ?? {}) };
|
|
57
|
+
if (matchesAny(rel, layout.guards?.codeShape?.exclude ?? []) || isContent(rel, layout)) return null;
|
|
58
|
+
const before = w.before ?? '';
|
|
59
|
+
const off = oneOffLines(w.after);
|
|
60
|
+
const fault = lineFault(lang, w.after, addedLines(before, w.after), off, limits) ?? functionFault(functionsOf(before, ext), functionsOf(w.after, ext), off, limits);
|
|
61
|
+
return fault ? `${fault}\nIf this one truly must stay, say so above it: \`one-off: <reason>\`.\n` : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const main = (text) => judgeEvent(text, { name: 'code-shape', write: judgeWrite });
|
|
65
|
+
|
|
66
|
+
function selftest() {
|
|
67
|
+
const hook = new URL(import.meta.url).pathname;
|
|
68
|
+
const body = (n) => Array.from({ length: n }, (_, i) => ` total = total + step(${i});\n`).join('');
|
|
69
|
+
const long = `void big() {\n${body(60)}}\n`;
|
|
70
|
+
const dir = scratchProject({ 'src/old.cpp': `void old() {\n${body(55)}}\n`, 'src/packed.h': 'void p() { a(); b(); c(); d(); }\n', 'kept/x.cpp': '// x\n', '.claude/layout.json': JSON.stringify({ guards: { codeShape: { exclude: ['kept/**'] } } }) });
|
|
71
|
+
const w = (file, content) => runHook(hook, dir, writeOf(`${dir}/${file}`, content));
|
|
72
|
+
const wide = `int x = ${'a + '.repeat(40)}b;\n`;
|
|
73
|
+
return verdicts('code-shape', [
|
|
74
|
+
['a function over the limit', w('src/a.cpp', long), 2],
|
|
75
|
+
['the same in python', w('src/a.py', `def big(rows):\n${Array.from({ length: 60 }, (_, i) => ` total = step(${i})\n`).join('')}`), 2],
|
|
76
|
+
['too many parameters', w('src/b.cpp', 'void f(int a, int b, int c, int d, int e, int f, int g) {\n run(a);\n}\n'), 2],
|
|
77
|
+
['a self parameter is not counted', w('src/b.py', 'def f(self, a, b, c, d, e, g):\n run(a)\n'), 0],
|
|
78
|
+
['packed statements', w('src/c.cpp', 'void f() {\n a(); b(); c();\n}\n'), 2],
|
|
79
|
+
['a for header is one statement', w('src/d.cpp', 'void f() {\n for (int i = 0; i < n; i++) total += v(i);\n}\n'), 0],
|
|
80
|
+
['a wide line', w('src/e.cpp', wide), 2],
|
|
81
|
+
['a wide comment is no code', w('src/f.cpp', `// ${'word '.repeat(50)}\nint x = 1;\n`), 0],
|
|
82
|
+
['an old long function, edited without growing', runHook(hook, dir, editOf(`${dir}/src/old.cpp`, 'step(3)', 'step(4)')), 0],
|
|
83
|
+
['an old long function that grows', runHook(hook, dir, editOf(`${dir}/src/old.cpp`, ' total = total + step(3);\n', ' total = total + step(3);\n total = total + step(99);\n')), 2],
|
|
84
|
+
['an old packed line that is edited', runHook(hook, dir, editOf(`${dir}/src/packed.h`, 'void p()', 'void q()')), 2],
|
|
85
|
+
['a marked one-off', w('src/g.cpp', `// one-off: the table is generated by hand\n${wide}`), 0],
|
|
86
|
+
['a test file', w('tests/x_test.cpp', long), 0],
|
|
87
|
+
['an excluded folder', w('kept/y.cpp', long), 0],
|
|
88
|
+
['a tutorial sample in a content folder', w('articles/guide/y.cpp', long), 0],
|
|
89
|
+
['a blog route is code', w('src/app/blog/page.cpp', long), 2],
|
|
90
|
+
['a heredoc that adds a packed line', runHook(hook, dir, bashOf("cat > src/h.cpp <<'EOF'\nvoid f() { a(); b(); c(); }\nEOF", dir)), 2],
|
|
91
|
+
]);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const hook = main; // run-hooks.mjs calls it with the event's text (KWB-24)
|
|
95
|
+
if (isMain(import.meta.url)) process.exit(process.argv.includes('--selftest') ? selftest() : await asHook('code-shape', main));
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PreToolUse (Write|Edit|MultiEdit|NotebookEdit|Bash): a kind is dispatched in ONE place (v7 additions, 0.11.0).
|
|
3
|
+
// When a write ADDS a `switch` on some kind (mode, type, action, format…) and another code file of the same checkout already
|
|
4
|
+
// switches on that kind, the kind's behaviour is being spread out: the next new kind then needs an edit in every one of those
|
|
5
|
+
// places. Refused: the kind's behaviour belongs in one object, table or class (rule: behaviour-lives-with-its-kind).
|
|
6
|
+
// Every write route (lib/writes.mjs judgeEvent); a file is judged against its own checkout (a worktree against the worktree);
|
|
7
|
+
// tests, `protected` globs, vendored and built folders are never scanned. `--selftest` proves it.
|
|
8
|
+
import { readFileSync } from 'node:fs';
|
|
9
|
+
import { extname, join } from 'node:path';
|
|
10
|
+
import { blankCode } from './lib/cpp-scan.mjs';
|
|
11
|
+
import { CODE, TESTS, codeFiles } from './lib/elements.mjs';
|
|
12
|
+
import { matchesAny } from './lib/glob.mjs';
|
|
13
|
+
import { readLayout } from './lib/layout.mjs';
|
|
14
|
+
import { checkoutRoot, claudeRoot } from './lib/project.mjs';
|
|
15
|
+
import { bashOf, editOf, runHook, scratchProject, verdicts, writeOf } from './lib/selftest.mjs';
|
|
16
|
+
import { judgeEvent } from './lib/writes.mjs';
|
|
17
|
+
import { asHook, isMain } from './lib/hook-log.mjs';
|
|
18
|
+
|
|
19
|
+
const SWITCH = /\bswitch\s*\(\s*([^()]*?(?:\([^()]*\))?)\s*\)\s*\{/g;
|
|
20
|
+
|
|
21
|
+
// The kind a switch dispatches on: the last name in its expression (args.kind -> kind, p->mode() -> mode).
|
|
22
|
+
export function kindsSwitched(text) {
|
|
23
|
+
const out = [];
|
|
24
|
+
for (const m of blankCode(text).matchAll(SWITCH)) {
|
|
25
|
+
const name = m[1].replace(/\(\s*\)$/, '').split(/\?\.|\.|->|::/).pop().trim();
|
|
26
|
+
if (/^[A-Za-z_$][\w$]{2,}$/.test(name)) out.push(name);
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const count = (list) => list.reduce((m, k) => m.set(k, (m.get(k) ?? 0) + 1), new Map());
|
|
32
|
+
|
|
33
|
+
// The kinds a write switches on more often than before.
|
|
34
|
+
export function newKinds(before, after) {
|
|
35
|
+
const was = count(kindsSwitched(before));
|
|
36
|
+
return [...count(kindsSwitched(after))].filter(([k, n]) => n > (was.get(k) ?? 0)).map(([k]) => k);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// { kind -> [files] }: the other code files of the checkout that already switch on each kind.
|
|
40
|
+
export function elsewhere(root, selfRel, kinds, layout = readLayout(root)) {
|
|
41
|
+
const skip = [...TESTS, ...(layout.protected ?? [])];
|
|
42
|
+
const found = new Map();
|
|
43
|
+
for (const rel of codeFiles(root, skip)) {
|
|
44
|
+
if (rel === selfRel) continue;
|
|
45
|
+
const there = new Set(kindsSwitched(readFileSync(join(root, rel), 'utf8')));
|
|
46
|
+
for (const k of kinds) if (there.has(k)) found.set(k, [...(found.get(k) ?? []), rel]);
|
|
47
|
+
}
|
|
48
|
+
return found;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// One write: null when it adds no second place for a kind; else the refusal.
|
|
52
|
+
function judgeWrite(w, rel) {
|
|
53
|
+
if (w.op === 'delete' || w.after === null || w.after === undefined || !CODE.has(extname(w.path)) || matchesAny(rel, TESTS)) return null;
|
|
54
|
+
const kinds = newKinds(w.before ?? '', w.after);
|
|
55
|
+
if (kinds.length === 0) return null;
|
|
56
|
+
const found = elsewhere(checkoutRoot(w.path), rel, kinds, readLayout(claudeRoot(w.path)));
|
|
57
|
+
if (found.size === 0) return null;
|
|
58
|
+
const lines = [...found].map(([k, files]) => ` "${k}" is already switched on in: ${files.slice(0, 5).join(', ')}`).join('\n');
|
|
59
|
+
return `${rel} adds a second place that branches on the same kind.\n${lines}\n` +
|
|
60
|
+
'Give each kind its own object, table entry or class (rule: behaviour-lives-with-its-kind), and call it from both places,\n' +
|
|
61
|
+
'so a new kind is one new entry instead of an edit in every switch.\n';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const main = (text) => judgeEvent(text, { name: 'dispatch-once', write: judgeWrite });
|
|
65
|
+
|
|
66
|
+
function selftest() {
|
|
67
|
+
const hook = new URL(import.meta.url).pathname;
|
|
68
|
+
const dir = scratchProject({
|
|
69
|
+
'src/render.cpp': 'float draw (Shape& s) { switch (s.mode) { case Mode::line: return 1.0f; default: return 0.0f; } }\n',
|
|
70
|
+
'tests/render_test.cpp': 'void t (Shape& s) { switch (s.format) { default: break; } }\n',
|
|
71
|
+
});
|
|
72
|
+
const w = (file, content) => runHook(hook, dir, writeOf(join(dir, file), content));
|
|
73
|
+
return verdicts('dispatch-once', [
|
|
74
|
+
['a new switch on a kind switched in another file', w('src/label.cpp', 'void label (Shape& s) { switch (s.mode) { default: break; } }\n'), 2],
|
|
75
|
+
['a switch on a kind switched nowhere else', w('src/order.cpp', 'void order (Row& r) { switch (r.direction) { default: break; } }\n'), 0],
|
|
76
|
+
['the same file is not "elsewhere"', runHook(hook, dir, editOf(join(dir, 'src/render.cpp'), 'return 1.0f;', 'return 2.0f;')), 0],
|
|
77
|
+
['a kind switched only in a test is not dispatch', w('src/save.cpp', 'void save (Shape& s) { switch (s.format) { default: break; } }\n'), 0],
|
|
78
|
+
['a test file may switch on any kind', w('tests/label_test.cpp', 'void t (Shape& s) { switch (s.mode) { default: break; } }\n'), 0],
|
|
79
|
+
['a switch in a comment or string is not a switch', w('src/note.ts', '// switch (x.mode) {\nconst s = "switch (x.mode) {";\n'), 0],
|
|
80
|
+
['the kind is the last name in the expression', w('src/args.ts', 'function f(args) { switch (args?.shape.mode) { default: } }\n'), 2],
|
|
81
|
+
['a heredoc that adds a second switch', runHook(hook, dir, bashOf("cat > src/b.cpp <<'EOF'\nvoid b (Shape& s) { switch (s.mode) { default: break; } }\nEOF", dir)), 2],
|
|
82
|
+
['another project is not ours', runHook(hook, dir, writeOf(join(dir, '..', 'elsewhere', 'x.cpp'), 'void x (S& s) { switch (s.mode) {} }\n')), 0],
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const hook = main; // run-hooks.mjs calls it with the event's text (KWB-24)
|
|
87
|
+
if (isMain(import.meta.url)) process.exit(process.argv.includes('--selftest') ? selftest() : await asHook('dispatch-once', main));
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PreToolUse (Write|Edit|MultiEdit|NotebookEdit|Bash): no source file grows past the line limit, by any route
|
|
3
|
+
// (a tool, or the shell's >, >>, tee, sed -i, cp, mv, a heredoc; lib/writes.mjs).
|
|
4
|
+
// A PostToolUse warning comes too late (the file has already grown and the extraction is rework),
|
|
5
|
+
// so this refuses the edit before it lands. Shrinking or keeping an already-long file is allowed,
|
|
6
|
+
// so splitting a big file is never blocked. Limit: layout.maxFileLines, default 300.
|
|
7
|
+
// `--selftest` proves it.
|
|
8
|
+
import { readFileSync } from 'node:fs';
|
|
9
|
+
import { extname, relative } from 'node:path';
|
|
10
|
+
import { writesOf } from './lib/writes.mjs';
|
|
11
|
+
import { matchesAny } from './lib/glob.mjs';
|
|
12
|
+
import { checkoutRoot, claudeRoot } from './lib/project.mjs';
|
|
13
|
+
import { readLayout } from './lib/layout.mjs';
|
|
14
|
+
import { verdicts } from './lib/selftest.mjs';
|
|
15
|
+
import { isContent } from './lib/content.mjs';
|
|
16
|
+
import { asHook, isMain } from './lib/hook-log.mjs';
|
|
17
|
+
|
|
18
|
+
const ROOT = process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
|
|
19
|
+
// Code, scripts, shaders and CMake files (Stellix's source rule counted scripts and CMake too); docs and data are not code.
|
|
20
|
+
const SOURCE = new Set(['.c', '.cc', '.cpp', '.cxx', '.h', '.hh', '.hpp', '.mm', '.m', '.inc', '.inl', '.ipp', '.tpp', '.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx', '.py', '.wgsl', '.metal', '.hlsl', '.glsl', '.vert', '.frag', '.sh', '.bash', '.cmake']);
|
|
21
|
+
const isSource = (rel) => SOURCE.has(extname(rel)) || /(^|\/)CMakeLists\.txt$/.test(rel);
|
|
22
|
+
const EXEMPT = ['**/*.generated.*', '**/generated/**', 'build/**', '_deps/**'];
|
|
23
|
+
|
|
24
|
+
const lines = (text) => (text === '' ? 0 : text.split('\n').length - (text.endsWith('\n') ? 1 : 0));
|
|
25
|
+
|
|
26
|
+
export function verdict(rel, before, after, limit) {
|
|
27
|
+
if (!isSource(rel) || matchesAny(rel, EXEMPT) || isContent(rel)) return null;
|
|
28
|
+
const n = lines(after);
|
|
29
|
+
if (n <= limit || n <= lines(before)) return null;
|
|
30
|
+
return `BLOCKED by file-size-guard: ${rel} would be ${n} lines (limit ${limit}).\n` +
|
|
31
|
+
'Split it before adding more: move a cohesive part (a class, a group of pure functions, a table) into its own file in the same folder, then make this edit.\n';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function main(text) {
|
|
35
|
+
let payload;
|
|
36
|
+
try { payload = JSON.parse(text); } catch { return 0; }
|
|
37
|
+
for (const w of writesOf(payload)) {
|
|
38
|
+
if (w.after === null || relative(ROOT, w.path).startsWith('..')) continue;
|
|
39
|
+
const rel = relative(checkoutRoot(w.path), w.path);
|
|
40
|
+
const msg = verdict(rel, w.before, w.after, readLayout(claudeRoot(w.path)).maxFileLines ?? 300);
|
|
41
|
+
if (msg) { process.stderr.write(msg); return 2; }
|
|
42
|
+
}
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function selftest() {
|
|
47
|
+
const big = 'x\n'.repeat(300);
|
|
48
|
+
const cases = [
|
|
49
|
+
['grows past the limit', verdict('src/core/Svf.cpp', big, `${big}y\n`, 300) !== null],
|
|
50
|
+
['at the limit is fine', verdict('src/core/Svf.cpp', '', big, 300) === null],
|
|
51
|
+
['shrinking a long file is allowed', verdict('src/core/Old.cpp', 'x\n'.repeat(500), 'x\n'.repeat(400), 300) === null],
|
|
52
|
+
['docs are not source', verdict('docs/spec.md', '', 'x\n'.repeat(900), 300) === null],
|
|
53
|
+
['generated code is exempt', verdict('src/gen/Tables.generated.h', '', 'x\n'.repeat(900), 300) === null],
|
|
54
|
+
['a shell script is code', verdict('tools/plants.sh', '', 'x\n'.repeat(301), 300) !== null],
|
|
55
|
+
['a CMakeLists.txt is code', verdict('lib/app/CMakeLists.txt', '', 'x\n'.repeat(301), 300) !== null],
|
|
56
|
+
];
|
|
57
|
+
return verdicts('file-size-guard', cases.map(([label, ok]) => [label, ok, true]));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const hook = main; // run-hooks.mjs calls it with the event's text (KWB-24)
|
|
61
|
+
if (isMain(import.meta.url)) process.exit(process.argv.includes('--selftest') ? selftest() : await asHook('file-size-guard', main));
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PreToolUse (Write|Edit|MultiEdit|NotebookEdit|Bash): a NEW source file may only be created inside a layout folder,
|
|
3
|
+
// by any route that creates a file: the Write tool, or the shell (>, >>, tee, cp, mv, touch, a heredoc into a file;
|
|
4
|
+
// lib/writes.mjs). An Edit, MultiEdit, NotebookEdit or sed -i never creates a file, so it is never judged here.
|
|
5
|
+
// The layout lives in .claude/layout.json and is summarised in CLAUDE.md; this makes it structural.
|
|
6
|
+
// Existing files can always be edited; moving them is a separate, deliberate job. `--selftest` proves it.
|
|
7
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { dirname, join, relative } from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { checkoutRoot, claudeRoot } from './lib/project.mjs';
|
|
11
|
+
import { codeFolders, readLayout } from './lib/layout.mjs';
|
|
12
|
+
import { bashOf, editOf, runHook, scratchProject, verdicts, writeOf } from './lib/selftest.mjs';
|
|
13
|
+
import { judgeEvent } from './lib/writes.mjs';
|
|
14
|
+
import { asHook, isMain } from './lib/hook-log.mjs';
|
|
15
|
+
|
|
16
|
+
const ROOT = process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
|
|
17
|
+
const SOURCE = /\.(c|cc|cpp|cxx|h|hh|hpp|mm|m|wgsl|metal|glsl|hlsl|slang)$/i;
|
|
18
|
+
|
|
19
|
+
// What is source: layout.json "sourceFiles" (a regex the configuration writes, e.g. full-stack's TypeScript; KWB-37), else SOURCE.
|
|
20
|
+
export const isSource = (file) => { const re = readLayout(claudeRoot(file)).sourceFiles; return (re ? new RegExp(re, 'i') : SOURCE).test(file); };
|
|
21
|
+
|
|
22
|
+
const main = (text) => judgeEvent(text, {
|
|
23
|
+
name: 'folder-law',
|
|
24
|
+
write: (w) => (w.op === 'delete' || w.op === 'edit' || existsSync(w.path) || !isSource(w.path) ? null : judge(w.path)),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
function judge(file) {
|
|
28
|
+
if (relative(ROOT, file).startsWith('..')) return null;
|
|
29
|
+
const root = checkoutRoot(file);
|
|
30
|
+
const rel = relative(root, file).replace(/\\/g, '/');
|
|
31
|
+
if (rel.startsWith('..')) return null;
|
|
32
|
+
|
|
33
|
+
const layout = readLayout(claudeRoot(file));
|
|
34
|
+
const folders = codeFolders(layout);
|
|
35
|
+
if (folders.length === 0) return null;
|
|
36
|
+
if (folders.some((f) => rel.startsWith(`${f}/`))) {
|
|
37
|
+
// Inside src/, the file must be in one of the named subfolders, not loose in src/ itself.
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const list = Object.values(layout.folders)
|
|
42
|
+
.filter((f) => folders.includes(f.path))
|
|
43
|
+
.map((f) => ` ${f.path}/ ${f.purpose}`)
|
|
44
|
+
.join('\n');
|
|
45
|
+
return `BLOCKED by folder-law: ${rel} is outside the project layout.\nNew source files go in one of:\n${list}\n` +
|
|
46
|
+
'If none fits, ask the user; a new folder is added to .claude/layout.json, then `npx --no-install @thedecipherist/claude-packs update`.\n';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function selftest() {
|
|
50
|
+
const hook = fileURLToPath(import.meta.url);
|
|
51
|
+
const dir = scratchProject({ 'stray/Old.cpp': '// old\n', 'x.txt': 'int x;\n' }, { folders: { core: { path: 'src/core', purpose: 'core code' }, docs: { path: 'docs', purpose: 'docs' } }, concepts: {} });
|
|
52
|
+
const w = (file) => runHook(hook, dir, writeOf(join(dir, file)));
|
|
53
|
+
const sh = (cmd) => runHook(hook, dir, bashOf(cmd, dir));
|
|
54
|
+
const ts = scratchProject({}, { folders: { api: { path: 'api/src', purpose: 'the API' } }, concepts: {}, sourceFiles: '\\.(ts|tsx)$' });
|
|
55
|
+
return verdicts('folder-law', [
|
|
56
|
+
['TypeScript is not source where the layout does not say so', w('stray/x.ts'), 0],
|
|
57
|
+
['a layout whose sourceFiles name TypeScript: a new .ts outside its folders (KWB-37)', runHook(hook, ts, writeOf(join(ts, 'stray/x.ts'))), 2],
|
|
58
|
+
['and inside them', runHook(hook, ts, writeOf(join(ts, 'api/src/x.ts'))), 0],
|
|
59
|
+
['a new file in a layout folder', w('src/core/Svf.cpp'), 0],
|
|
60
|
+
['a new file outside the layout', w('stray/New.cpp'), 2],
|
|
61
|
+
['a new file loose in src/', w('src/Loose.cpp'), 2],
|
|
62
|
+
['an existing file outside the layout', w('stray/Old.cpp'), 0],
|
|
63
|
+
['an edit is never a new file', runHook(hook, dir, editOf(join(dir, 'stray/New.cpp'))), 0],
|
|
64
|
+
['a doc is not source', w('stray/notes.md'), 0],
|
|
65
|
+
['another project is not ours', runHook(hook, dir, writeOf(join(dirname(dir), 'elsewhere/x.cpp'))), 0],
|
|
66
|
+
['a heredoc into a new file outside the layout', sh("cat > stray/B.cpp <<'EOF'\nint b;\nEOF"), 2],
|
|
67
|
+
['a copy into a layout folder', sh('cp x.txt src/core/C.cpp'), 0],
|
|
68
|
+
['a move outside the layout', sh('mv x.txt stray/D.cpp'), 2],
|
|
69
|
+
['words about a file are no write', sh("echo 'cat > stray/E.cpp'"), 0],
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const hook = main; // run-hooks.mjs calls it with the event's text (KWB-24)
|
|
74
|
+
if (isMain(import.meta.url)) process.exit(process.argv.includes('--selftest') ? selftest() : await asHook('folder-law', main));
|