@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,11 @@
|
|
|
1
|
+
// What a reader reads as prose in a markdown file (0.15.0): frontmatter, fenced code, inline code, HTML comments, link targets and URLs
|
|
2
|
+
// are blanked, line for line, so a check on public text never reads `npm test -- x` as a double dash. The same length and lines stay.
|
|
3
|
+
const blank = (s) => s.replace(/[^\n]/g, ' ');
|
|
4
|
+
|
|
5
|
+
export const blankProse = (text) => text
|
|
6
|
+
.replace(/^---\n[\s\S]*?\n---\n/, blank)
|
|
7
|
+
.replace(/^(```+|~~~+)[^\n]*\n[\s\S]*?^\1[^\n]*$/gm, blank)
|
|
8
|
+
.replace(/<!--[\s\S]*?-->/g, blank)
|
|
9
|
+
.replace(/`[^`\n]*`/g, blank)
|
|
10
|
+
.replace(/\]\([^)\n]*\)/g, blank)
|
|
11
|
+
.replace(/https?:\/\/\S+/g, blank);
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// pushed: what a repo holds that GitHub does not (KWB-26: eight kit branches lived on one machine only, and nothing said so).
|
|
2
|
+
// git-guard reads it twice: before a `git commit` (the branch's earlier commits must be on GitHub; the first commit of a new
|
|
3
|
+
// branch passes, the next waits for its push) and at Stop (no local branch may be ahead of GitHub or missing there).
|
|
4
|
+
// GitHub's branch heads come from `git ls-remote --heads <remote>`: no objects are fetched and nothing local changes. The last
|
|
5
|
+
// look is kept in the repo's git dir (claude-packs-heads.json) and only ever lets work through: a commit seen on GitHub stays
|
|
6
|
+
// there (the house laws never force a push or delete a branch), so a commit or a Stop the last look already clears makes no
|
|
7
|
+
// network call, however old the look. Anything it would refuse is looked up again live first, so a push made seconds
|
|
8
|
+
// ago is seen at once. (Measured 2026-11-03 at load 27: a Stop the cache clears 0.3 s; a live look 4 to 7 s.)
|
|
9
|
+
// A private GitHub repo needs the token: it is read from the environment or ~/.env (guards.git.tokenVar) and handed to git
|
|
10
|
+
// as an Authorization header through the child's environment (GIT_CONFIG_COUNT/KEY/VALUE: never on its command line, so
|
|
11
|
+
// never in ps); it is never written to a remote, a config or a file. Protocol v0 answers with every head in one request
|
|
12
|
+
// with the header sent at once (measured 2026-11-03: 4 s against 12 s for v2 with a credential helper, GitHub slow).
|
|
13
|
+
// GitHub out of reach (offline, no token): the caller lets the work through and says so.
|
|
14
|
+
// A repo that pushes by URL only and has no remote (Stellix) names its GitHub repo in guards.git.repo ("owner/name", or a URL
|
|
15
|
+
// or path); with neither, there is nothing to compare with and nothing is judged.
|
|
16
|
+
// remoteHeads(cwd, remote, { tokenVar, repo, live }) -> { url, heads: Map<branch, sha>, cached } | { url, error }
|
|
17
|
+
// forRepo(cwd, projectDir, cfg) -> cfg, without guards.git.repo when cwd is another repo
|
|
18
|
+
// unpushed(cwd, branch, cfg) -> null | { error } | { branch, remote, url, commits: [{ sha, subject }] }
|
|
19
|
+
// localBranches(cwd, cfg) -> null | { error } | [{ branch, url, missing, ahead }] (only the ones not on GitHub)
|
|
20
|
+
// pushCommand(url, branches, cfg) -> the push form with the token named
|
|
21
|
+
import { execFileSync } from 'node:child_process';
|
|
22
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
23
|
+
import { homedir } from 'node:os';
|
|
24
|
+
import { join, resolve } from 'node:path';
|
|
25
|
+
|
|
26
|
+
const CACHE = 'claude-packs-heads.json';
|
|
27
|
+
|
|
28
|
+
const git = (cwd, args, env = {}, input = undefined) => {
|
|
29
|
+
try {
|
|
30
|
+
return execFileSync('git', args, { cwd, encoding: 'utf8', input, stdio: [input === undefined ? 'ignore' : 'pipe', 'pipe', 'pipe'], timeout: 15000, env: { ...process.env, GIT_TERMINAL_PROMPT: '0', ...env } });
|
|
31
|
+
} catch (e) {
|
|
32
|
+
return { error: (e.signal ? 'no answer in 15 s' : String(e.stderr ?? e.message).trim().split('\n').pop()) || 'git failed' };
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const out = (r) => (typeof r === 'string' ? r.trim() : null);
|
|
36
|
+
|
|
37
|
+
export function tokenFor(tokenVar) {
|
|
38
|
+
if (process.env[tokenVar]) return process.env[tokenVar];
|
|
39
|
+
try {
|
|
40
|
+
const m = readFileSync(join(homedir(), '.env'), 'utf8').match(new RegExp(`^\\s*(?:export\\s+)?${tokenVar}=(['"]?)(.*?)\\1\\s*$`, 'm'));
|
|
41
|
+
return m?.[2] || null;
|
|
42
|
+
} catch { return null; }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const isGitHub = (url) => /^https:\/\/([^@/]+@)?github\.com\//.test(url ?? '');
|
|
46
|
+
const cacheFile = (cwd) => { const d = out(git(cwd, ['rev-parse', '--git-common-dir'])); return d ? join(resolve(cwd, d), CACHE) : null; };
|
|
47
|
+
const readCache = (file) => { try { return JSON.parse(readFileSync(file, 'utf8')); } catch { return {}; } };
|
|
48
|
+
|
|
49
|
+
// the URL a remote name stands for, else the layout's repo
|
|
50
|
+
export function urlOf(cwd, remote, { repo = null } = {}) {
|
|
51
|
+
const url = out(git(cwd, ['remote', 'get-url', remote]));
|
|
52
|
+
if (url || !repo) return url || null;
|
|
53
|
+
return /^(https?:|file:|\/)/.test(repo) ? repo : `https://github.com/${repo.replace(/\.git$/, '')}.git`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function remoteHeads(cwd, remote, { tokenVar = 'PERSONAL_GITHUB_TOKEN', repo = null, live = false, now = Date.now() } = {}) {
|
|
57
|
+
const url = urlOf(cwd, remote, { repo });
|
|
58
|
+
if (!url) return { url: null, error: `no remote named ${remote}` };
|
|
59
|
+
const file = cacheFile(cwd);
|
|
60
|
+
const cache = file ? readCache(file) : {};
|
|
61
|
+
const hit = cache[url];
|
|
62
|
+
if (!live && hit) return { url, heads: new Map(Object.entries(hit.heads)), cached: true };
|
|
63
|
+
const args = ['-c', 'protocol.version=0', 'ls-remote', '--heads', url];
|
|
64
|
+
let env = {};
|
|
65
|
+
const token = isGitHub(url) ? tokenFor(tokenVar) : null;
|
|
66
|
+
if (token) {
|
|
67
|
+
const basic = Buffer.from(`x-access-token:${token}`).toString('base64');
|
|
68
|
+
env = { GIT_CONFIG_COUNT: '2', GIT_CONFIG_KEY_0: 'http.extraHeader', GIT_CONFIG_VALUE_0: `Authorization: Basic ${basic}`, GIT_CONFIG_KEY_1: 'credential.helper', GIT_CONFIG_VALUE_1: '' };
|
|
69
|
+
}
|
|
70
|
+
const r = git(cwd, args, env);
|
|
71
|
+
if (typeof r !== 'string') return { url, error: r.error };
|
|
72
|
+
const heads = new Map(r.split('\n').filter(Boolean).map((l) => l.split('\t')).map(([sha, ref]) => [ref.replace(/^refs\/heads\//, ''), sha]));
|
|
73
|
+
if (file) { try { writeFileSync(file, JSON.stringify({ ...cache, [url]: { time: now, heads: Object.fromEntries(heads) } })); } catch { /* the check still holds */ } }
|
|
74
|
+
return { url, heads, cached: false };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// The remote a branch pushes to and its name there: its upstream, else origin and the same name.
|
|
78
|
+
function target(cwd, branch) {
|
|
79
|
+
const remote = out(git(cwd, ['config', `branch.${branch}.remote`]));
|
|
80
|
+
const merge = out(git(cwd, ['config', `branch.${branch}.merge`]));
|
|
81
|
+
if (remote && remote !== '.') return { remote, name: merge ? merge.replace(/^refs\/heads\//, '') : branch };
|
|
82
|
+
return { remote: 'origin', name: branch };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// the heads whose commits this repo has (one git call for all of them: a call each took 20 s at load 27 on 28 branches)
|
|
86
|
+
const known = (cwd, heads) => {
|
|
87
|
+
const shas = [...new Set(heads.values())];
|
|
88
|
+
const r = out(git(cwd, ['cat-file', '--batch-check'], {}, shas.join('\n') + '\n'));
|
|
89
|
+
return r === null ? [] : r.split('\n').filter((l) => / commit /.test(l)).map((l) => l.split(' ')[0]);
|
|
90
|
+
};
|
|
91
|
+
// commits on `ref` that no GitHub branch holds (only heads whose commits this repo has can be subtracted)
|
|
92
|
+
const ownCommits = (cwd, ref, heads, have = known(cwd, heads)) => {
|
|
93
|
+
const r = out(git(cwd, ['log', '--format=%h %s', ref, '--not', ...have, '--']));
|
|
94
|
+
return r === null ? null : r.split('\n').filter(Boolean).map((l) => ({ sha: l.slice(0, l.indexOf(' ')), subject: l.slice(l.indexOf(' ') + 1) }));
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Looks with the cache first; anything it would refuse is looked up again live.
|
|
98
|
+
function withHeads(cwd, remote, cfg, judge) {
|
|
99
|
+
let h = remoteHeads(cwd, remote, cfg);
|
|
100
|
+
if (h.error) return { error: `${h.url ?? remote} could not be reached (${h.error})` };
|
|
101
|
+
let found = judge(h);
|
|
102
|
+
if (found && h.cached) {
|
|
103
|
+
h = remoteHeads(cwd, remote, { ...cfg, live: true });
|
|
104
|
+
if (h.error) return { error: `${h.url} could not be reached (${h.error})` };
|
|
105
|
+
found = judge(h);
|
|
106
|
+
}
|
|
107
|
+
return found;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// guards.git.repo names the project's own repo: a command in another repo is not judged against it (KWB-48; it told a repo with
|
|
111
|
+
// no remote to push its history into the project's repo). A worktree of the project shares its git dir, so it stays judged.
|
|
112
|
+
export function forRepo(cwd, projectDir, cfg = {}) {
|
|
113
|
+
const common = (dir) => { const d = out(git(dir, ['rev-parse', '--git-common-dir'])); return d ? resolve(dir, d) : null; };
|
|
114
|
+
return cfg.repo && common(cwd) !== common(projectDir) ? { ...cfg, repo: null } : cfg;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function unpushed(cwd, branch, cfg = {}) {
|
|
118
|
+
if (!branch || branch === 'HEAD' || out(git(cwd, ['rev-parse', '-q', '--verify', 'HEAD'])) === null) return null;
|
|
119
|
+
const { remote } = target(cwd, branch);
|
|
120
|
+
if (!urlOf(cwd, remote, cfg)) return null; // no remote and no guards.git.repo: nothing to compare with
|
|
121
|
+
return withHeads(cwd, remote, cfg, (h) => {
|
|
122
|
+
const commits = ownCommits(cwd, 'HEAD', h.heads);
|
|
123
|
+
return commits?.length ? { branch, remote, url: h.url, commits } : null;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function localBranches(cwd, cfg = {}) {
|
|
128
|
+
// every branch, its commit and its upstream in one call
|
|
129
|
+
const r = out(git(cwd, ['for-each-ref', '--format=%(refname:short)%09%(objectname)%09%(upstream:remotename)%09%(upstream:remoteref)', 'refs/heads']));
|
|
130
|
+
if (r === null) return null;
|
|
131
|
+
const byRemote = new Map();
|
|
132
|
+
for (const [branch, sha, up, ref] of r.split('\n').filter(Boolean).map((l) => l.split('\t'))) {
|
|
133
|
+
if (cfg.trunkNeedsYes && (cfg.trunks ?? []).includes(branch)) continue; // only the owner pushes it
|
|
134
|
+
const remote = up && up !== '.' ? up : 'origin';
|
|
135
|
+
const name = up && up !== '.' && ref ? ref.replace(/^refs\/heads\//, '') : branch;
|
|
136
|
+
if (!byRemote.has(remote)) byRemote.set(remote, []);
|
|
137
|
+
byRemote.get(remote).push({ branch, sha, name });
|
|
138
|
+
}
|
|
139
|
+
const found = [];
|
|
140
|
+
for (const [remote, branches] of byRemote) {
|
|
141
|
+
if (!urlOf(cwd, remote, cfg)) continue;
|
|
142
|
+
const f = withHeads(cwd, remote, cfg, (h) => {
|
|
143
|
+
const off = [];
|
|
144
|
+
let have = null;
|
|
145
|
+
for (const { branch, sha: local, name } of branches) {
|
|
146
|
+
const sha = h.heads.get(name);
|
|
147
|
+
if (!sha) { off.push({ branch, url: h.url, missing: true }); continue; }
|
|
148
|
+
if (local === sha) continue;
|
|
149
|
+
have ??= known(cwd, h.heads);
|
|
150
|
+
const ahead = ownCommits(cwd, branch, h.heads, have)?.length ?? 0;
|
|
151
|
+
if (ahead) off.push({ branch, url: h.url, ahead });
|
|
152
|
+
}
|
|
153
|
+
return off.length ? off : null;
|
|
154
|
+
});
|
|
155
|
+
if (f?.error) return f;
|
|
156
|
+
if (f) found.push(...f);
|
|
157
|
+
}
|
|
158
|
+
return found;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// GitHub's push form, the token named and never stored; the repo is the remote's own name.
|
|
162
|
+
// The owner an SSH GitHub URL names (git@github.com:Owner/repo.git, an ssh config alias git@github-<name>:Owner/..., or
|
|
163
|
+
// ssh://git@github.com/Owner/...), else null. Over SSH the key picks the account, so the URL alone names the repo (KWB-39).
|
|
164
|
+
export function sshOwner(url) {
|
|
165
|
+
return (url ?? '').match(/^(?:ssh:\/\/)?git@github(?:\.com|-[\w.-]+)[:/]([\w.-]+)\/[\w.-]+?(?:\.git)?\/?$/)?.[1] ?? null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function pushCommand(url, branches, { owner, tokenVar = 'PERSONAL_GITHUB_TOKEN' } = {}) {
|
|
169
|
+
if (owner && sshOwner(url) === owner) return `git push ${url} ${branches.join(' ')}`;
|
|
170
|
+
const m = (url ?? '').match(/github\.com[:/]([^/]+)\/([^/\s]+?)(?:\.git)?\/?$/);
|
|
171
|
+
const repo = m ? `${m[1]}/${m[2]}` : `${owner ?? '<owner>'}/${(url ?? 'repo').split('/').pop().replace(/\.git$/, '')}`;
|
|
172
|
+
return `source ~/.env && git push https://x-access-token:$${tokenVar}@github.com/${repo}.git ${branches.join(' ')}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const NEVER_DELETE = 'A branch is never deleted, renamed or left behind to get past this: push it.';
|
|
176
|
+
|
|
177
|
+
export function unpushedMessage(u, cfg = {}) {
|
|
178
|
+
const list = u.commits.slice(0, 8).map((c) => ` ${c.sha} ${c.subject}`).join('\n') + (u.commits.length > 8 ? `\n ... ${u.commits.length - 8} more` : '');
|
|
179
|
+
return `${u.branch} has ${u.commits.length} commit(s) that are not on GitHub (${u.url}):\n${list}\n` +
|
|
180
|
+
`Every branch is pushed from its first commit (KWB-26): push, then commit:\n ${pushCommand(u.url, [u.branch], cfg)}\n${NEVER_DELETE}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Stop: -> { code, stdout, stderr }. Three refusals with the same list end the session, the list going to the user.
|
|
184
|
+
export const MAX_SENDS = 3;
|
|
185
|
+
export function stopVerdict(root, cfg = {}, session = 'session', stateDir = process.env.TMPDIR ?? '/tmp') {
|
|
186
|
+
const found = localBranches(root, cfg);
|
|
187
|
+
if (found?.error) return { code: 0, stdout: JSON.stringify({ systemMessage: `git-guard: GitHub was not checked at the end of this session: ${found.error}. Check by hand that every branch is pushed (KWB-26).` }) };
|
|
188
|
+
const file = join(stateDir, `git-guard-stop-${String(session).replace(/[^\w-]/g, '')}.json`);
|
|
189
|
+
if (!found?.length) { try { writeFileSync(file, '{}'); } catch { /* nothing to count */ } return { code: 0 }; }
|
|
190
|
+
const byUrl = new Map();
|
|
191
|
+
for (const f of found) byUrl.set(f.url, [...(byUrl.get(f.url) ?? []), f.branch]);
|
|
192
|
+
const text = `${found.length} local branch(es) are not on GitHub:\n` +
|
|
193
|
+
found.map((f) => ` ${f.branch}: ${f.missing ? 'not on GitHub at all' : `${f.ahead} commit(s) ahead of GitHub`}`).join('\n') +
|
|
194
|
+
`\nPush them before the session ends (KWB-26):\n${[...byUrl].map(([url, b]) => ` ${pushCommand(url, b, cfg)}`).join('\n')}\n${NEVER_DELETE}`;
|
|
195
|
+
let st = {};
|
|
196
|
+
try { st = JSON.parse(readFileSync(file, 'utf8')); } catch { /* first */ }
|
|
197
|
+
const count = st.text === text ? (st.count ?? 0) + 1 : 1;
|
|
198
|
+
try { writeFileSync(file, JSON.stringify({ text, count })); } catch { /* still refused */ }
|
|
199
|
+
if (count > MAX_SENDS) return { code: 0, stdout: JSON.stringify({ systemMessage: `git-guard refused the end of this session ${MAX_SENDS} times; it ends here for you to decide.\n${text}` }) };
|
|
200
|
+
return { code: 2, stderr: `git-guard: ${text}\n` };
|
|
201
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// What a checkout already holds, per file: its element shapes, its statements and its definitions, kept between calls in the temp folder and read
|
|
2
|
+
// again only for a file whose time or size moved. template-law judges a write against it; reuse-hint shows it at the prompt.
|
|
3
|
+
// repoIndex(root, skipGlobs, layout) -> { rel: { m, s, shapes: [[id, line, what]], st: [[hash, line]], defs: [[name, line]] } }
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import { extname, join } from 'node:path';
|
|
8
|
+
import { isContent } from './content.mjs';
|
|
9
|
+
import { MARKUP, codeFiles, shapesOf } from './elements.mjs';
|
|
10
|
+
import { ALL_CODE } from './languages.mjs';
|
|
11
|
+
import { definesOf, fingerprint } from './near-copies.mjs';
|
|
12
|
+
|
|
13
|
+
export const EXTS = new Set([...ALL_CODE, ...MARKUP]);
|
|
14
|
+
const GENERATED = /\b(?:auto-?generated|generated by|do not edit|@generated)\b/i;
|
|
15
|
+
|
|
16
|
+
// A `one-off:` line covers what follows it, down to the next blank line.
|
|
17
|
+
export function oneOffLines(text) {
|
|
18
|
+
const out = new Set();
|
|
19
|
+
let on = false;
|
|
20
|
+
text.split('\n').forEach((l, i) => { on = /one-off:/.test(l) || (on && l.trim() !== ''); if (on) out.add(i + 1); });
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// One object set up twice is one object; a copy of a set-up has another receiver.
|
|
25
|
+
const distinct = (list) => list.filter((s, i) => s.kind !== 'setup' || list.findIndex((x) => x.id === s.id && x.what === s.what) === i);
|
|
26
|
+
|
|
27
|
+
// What a text holds, with its marked one-offs left out: all the law ever compares.
|
|
28
|
+
export function readText(text, rel) {
|
|
29
|
+
if (GENERATED.test(text.slice(0, 400))) return { shapes: [], st: [], defs: [] };
|
|
30
|
+
const off = oneOffLines(text);
|
|
31
|
+
const ext = extname(rel);
|
|
32
|
+
const open = (list) => list.filter(([, line]) => !off.has(line));
|
|
33
|
+
return { shapes: distinct(shapesOf(text, ext).filter((s) => !off.has(s.line))), st: open(fingerprint(text, ext)), defs: open(definesOf(text, ext)) };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const fileOf = (root) => join(tmpdir(), `claude-packs-repo-${createHash('sha256').update(root).digest('hex').slice(0, 12)}.json`);
|
|
37
|
+
|
|
38
|
+
export function repoIndex(root, skip, layout = {}) {
|
|
39
|
+
const file = fileOf(root);
|
|
40
|
+
let old = {};
|
|
41
|
+
try { old = JSON.parse(readFileSync(file, 'utf8')); } catch { /* the first call builds it */ }
|
|
42
|
+
const now = {};
|
|
43
|
+
for (const rel of codeFiles(root, skip, EXTS).filter((f) => !isContent(f, layout))) {
|
|
44
|
+
const st = statSync(join(root, rel));
|
|
45
|
+
const had = old[rel];
|
|
46
|
+
if (had && had.m === st.mtimeMs && had.s === st.size) { now[rel] = had; continue; }
|
|
47
|
+
const read = readText(readFileSync(join(root, rel), 'utf8'), rel);
|
|
48
|
+
now[rel] = { m: st.mtimeMs, s: st.size, shapes: read.shapes.map((x) => [x.id, x.line, x.what]), st: read.st, defs: read.defs };
|
|
49
|
+
}
|
|
50
|
+
try { writeFileSync(file, JSON.stringify(now)); } catch { /* an index that cannot be kept is built again */ }
|
|
51
|
+
return now;
|
|
52
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// What a secret looks like: secrets-guard refuses these in a write, and the logs mask them, so a log can be read and shared.
|
|
2
|
+
// SECRETS: [[name, regex]] maskSecrets(text) -> text with each one replaced by its name
|
|
3
|
+
export const SECRETS = [
|
|
4
|
+
['a GitHub token', /\b(?:gh[pousr]_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{40,})/g],
|
|
5
|
+
['an Anthropic key', /\bsk-ant-[A-Za-z0-9_-]{20,}/g],
|
|
6
|
+
['an OpenAI key', /\bsk-(?!ant-)(?:proj-)?[A-Za-z0-9_-]{32,}/g],
|
|
7
|
+
['an AWS access key', /\bAKIA[0-9A-Z]{16}\b/g],
|
|
8
|
+
['a Slack token', /\bxox[abprs]-[A-Za-z0-9-]{10,}/g],
|
|
9
|
+
['a Stripe secret key', /\b[rs]k_live_[A-Za-z0-9]{20,}/g],
|
|
10
|
+
['a private key', /-----BEGIN (?:[A-Z]+ )?PRIVATE KEY-----\s+[A-Za-z0-9+/=\s]{64,}/g],
|
|
11
|
+
// A key's random part runs unbroken; a long identifier (`cap_Filter_Env_Attack_2`) is short words between underscores.
|
|
12
|
+
['a prefixed API key', /\b(?!price_|prod_|pk_|[rs]k_live_|github_pat_|gh[pousr]_)(?=[a-z]{2,8}_)(?=[\w-]*[A-Z])(?=[\w-]*\d)(?=[a-z]{2,8}_[\w-]*?[A-Za-z0-9]{20})[a-z]{2,8}_[A-Za-z0-9_-]{24,}\b/g],
|
|
13
|
+
['a connection string with its password', /\b(?:mongodb(?:\+srv)?|postgres(?:ql)?|mysql|redis|amqp):\/\/[^\s:@/'"`]+:(?!\$\{|\$[A-Za-z_]|<|\*+@|[^\s@]*(?:[Pp]ass(?:word|wd)?|[Cc]hange|[Ee]xample|[Yy]our|[Ss]ecret|[Pp]laceholder|xxx|XXX))[^\s@/'"`]{3,}@/g],
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const WORDS = /\b(password|passwd|secret|token|api[_-]?key)(\s*[=:]\s*)(['"]?)[^\s'"&]{4,}/gi;
|
|
17
|
+
|
|
18
|
+
export const maskSecrets = (text) => SECRETS.reduce((t, [name, re]) => t.replace(re, `[${name}]`), String(text)).replace(WORDS, '$1$2$3[masked]');
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// What a hook's `--selftest` runs on: a throwaway project, the hook run the way Claude Code runs
|
|
2
|
+
// it (payload on stdin, CLAUDE_PROJECT_DIR set) and its exit code judged. Every hook proves its
|
|
3
|
+
// law this way, so nobody probes a hook by pasting payloads into a shell command, where a guard
|
|
4
|
+
// that judges command text can refuse the probe itself (Sub37 WB-4). doctor runs every selftest.
|
|
5
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
6
|
+
import { existsSync, mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import { dirname, join } from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
|
|
11
|
+
const made = [];
|
|
12
|
+
|
|
13
|
+
export const put = (file, text) => { mkdirSync(dirname(file), { recursive: true }); writeFileSync(file, text); };
|
|
14
|
+
export const git = (cwd, ...a) => execFileSync('git', ['-c', 'user.email=selftest@local', '-c', 'user.name=selftest', ...a], { cwd, stdio: 'ignore' });
|
|
15
|
+
|
|
16
|
+
// files: { "rel/path": "text" }; layout, when given, becomes .claude/layout.json.
|
|
17
|
+
export function scratchProject(files = {}, layout = null) {
|
|
18
|
+
const dir = realpathSync(mkdtempSync(join(tmpdir(), 'cp-selftest-')));
|
|
19
|
+
made.push(dir);
|
|
20
|
+
if (layout) put(join(dir, '.claude/layout.json'), JSON.stringify(layout));
|
|
21
|
+
for (const [rel, text] of Object.entries(files)) put(join(dir, rel), text);
|
|
22
|
+
return dir;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// A hook event goes through run-hooks.mjs, the way settings.json runs every kit hook (KWB-24), so each selftest proves the
|
|
26
|
+
// hook inside the runner; a by-hand mode (args, e.g. --init) runs the hook file itself.
|
|
27
|
+
const RUNNER = fileURLToPath(new URL('../run-hooks.mjs', import.meta.url));
|
|
28
|
+
export const runHook = (hook, dir, payload, args = []) =>
|
|
29
|
+
spawnSync(process.execPath, args.length || !existsSync(RUNNER) ? [hook, ...args] : [RUNNER, hook], {
|
|
30
|
+
cwd: dir, input: payload ? JSON.stringify(payload) : '', encoding: 'utf8',
|
|
31
|
+
env: { ...process.env, CLAUDE_PROJECT_DIR: dir },
|
|
32
|
+
}).status;
|
|
33
|
+
|
|
34
|
+
export const writeOf = (file_path, content = '// new\n') => ({ tool_name: 'Write', tool_input: { file_path, content } });
|
|
35
|
+
export const editOf = (file_path, old_string = 'a', new_string = 'b') => ({ tool_name: 'Edit', tool_input: { file_path, old_string, new_string } });
|
|
36
|
+
export const bashOf = (command, cwd) => ({ tool_name: 'Bash', tool_input: { command }, ...(cwd ? { cwd } : {}) });
|
|
37
|
+
|
|
38
|
+
// cases: [label, actual, wanted]. Prints "<name> selftest: OK (n cases)" or each failure; exit code.
|
|
39
|
+
export function verdicts(name, cases) {
|
|
40
|
+
for (const dir of made.splice(0)) rmSync(dir, { recursive: true, force: true });
|
|
41
|
+
const bad = cases.filter(([, got, want]) => got !== want);
|
|
42
|
+
for (const [label, got, want] of bad) console.error(`${name} selftest FAILED: ${label} (got ${got}, wanted ${want})`);
|
|
43
|
+
if (!bad.length) console.log(`${name} selftest: OK (${cases.length} cases)`);
|
|
44
|
+
return bad.length ? 1 : 0;
|
|
45
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// shape-code: what the shape law reads in code beyond literal instances (lib/shape.mjs holds the number rule).
|
|
2
|
+
// findSelectorDefs(text, file, shape) -> [{ line, text, concept, name, why, kind: 'selector' }]
|
|
3
|
+
// a function whose name joins a concept word with pick/select/for/first/owner, defined here and not listed as a
|
|
4
|
+
// selector (or the generator, or a constant) of that concept in design/shape.json
|
|
5
|
+
// findUnknownTypes(text, file, shape) -> [{ line, text, concept: null, name, why, kind: 'type' }]
|
|
6
|
+
// a new class/struct/enum whose name holds no concept's word and that looks like an instance container (an index
|
|
7
|
+
// member, or it is made N times in an array): its concept has no row yet; a type in the manifest's "values" is skipped
|
|
8
|
+
import { conceptOfName, parts, SHAPE_FILE } from './shape.mjs';
|
|
9
|
+
|
|
10
|
+
export const CODE = /\.(c|cc|cpp|cxx|h|hh|hpp|hxx|inl|mm|m|js|mjs|cjs|ts|tsx|jsx|py|swift|rs|go|java|kt|cs)$/i;
|
|
11
|
+
const KEYWORDS = new Set(['if', 'for', 'while', 'switch', 'catch', 'return', 'sizeof', 'else', 'new', 'delete', 'case', 'throw', 'co_return', 'do', 'function', 'await', 'typeof']);
|
|
12
|
+
const PICKS = [(p) => p.startsWith('pick'), (p) => p.startsWith('select'), (p) => p.startsWith('owner') || p === 'owning', (p) => p === 'for', (p) => p === 'first'];
|
|
13
|
+
const ITERATES = new Set(['each', 'all', 'every', 'count', 'num', 'size']);
|
|
14
|
+
|
|
15
|
+
function definitions(text) {
|
|
16
|
+
const out = [];
|
|
17
|
+
const lines = text.split('\n');
|
|
18
|
+
const RES = [
|
|
19
|
+
/^\s*(?:(?:static|inline|constexpr|consteval|virtual|extern|friend|explicit|const|unsigned|signed|\[\[\w+\]\])\s+)*[A-Za-z_][\w:<>,*&]*\s*[*&]?\s+[*&]?([A-Za-z_]\w*)\s*\([^;{}]*\)\s*(?:const\s*)?(?:noexcept\s*)?(?:override\s*)?(?:->\s*[\w:<>*&\s]+)?\s*(?:\{|;|$)/,
|
|
20
|
+
/\bfunction\s*\*?\s*([A-Za-z_$][\w$]*)\s*\(/,
|
|
21
|
+
/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/,
|
|
22
|
+
/^\s*(?:static\s+|async\s+|get\s+)*([A-Za-z_$][\w$]*)\s*\([^)]*\)\s*\{/,
|
|
23
|
+
/^\s*(?:async\s+)?def\s+([A-Za-z_]\w*)\s*\(/,
|
|
24
|
+
/^\s*(?:pub\s+)?fn\s+([A-Za-z_]\w*)\s*[<(]/,
|
|
25
|
+
];
|
|
26
|
+
lines.forEach((l, i) => {
|
|
27
|
+
for (const re of RES) {
|
|
28
|
+
const m = l.match(re);
|
|
29
|
+
if (!m || KEYWORDS.has(m[1])) continue;
|
|
30
|
+
const first = l.trim().split(/\s+/)[0];
|
|
31
|
+
if (re === RES[0] && KEYWORDS.has(first)) continue;
|
|
32
|
+
out.push({ line: i + 1, text: l.trim(), name: m[1] });
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function findSelectorDefs(text, file, shape) {
|
|
40
|
+
if (!shape || !CODE.test(file ?? '')) return [];
|
|
41
|
+
const out = [];
|
|
42
|
+
for (const d of definitions(text)) {
|
|
43
|
+
const ps = parts(d.name);
|
|
44
|
+
if (!PICKS.some((f) => ps.some(f)) || ps.some((p) => ITERATES.has(p))) continue;
|
|
45
|
+
const concept = conceptOfName(d.name, shape);
|
|
46
|
+
if (!concept) continue;
|
|
47
|
+
const c = shape.concepts[concept];
|
|
48
|
+
if (c.shape !== 'many' || c.selectors.includes(d.name) || c.constants.includes(d.name) || c.generatorName === d.name) continue;
|
|
49
|
+
const listed = c.selectors.length ? `call a listed one (${c.selectors.join(', ')}) or ` : '';
|
|
50
|
+
out.push({ ...d, concept, kind: 'selector', why: `defines ${d.name}, a selector for ${concept} that ${SHAPE_FILE} does not list — example, rule, or constant? (a rule is a named selector: ${listed}add "${d.name}": "<what it picks>" to the ${concept} row's selectors first)` });
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function bodyOf(lines, i, python) {
|
|
56
|
+
if (python) {
|
|
57
|
+
const ind = lines[i].match(/^\s*/)[0].length;
|
|
58
|
+
const body = [];
|
|
59
|
+
for (let k = i + 1; k < lines.length && (lines[k].trim() === '' || lines[k].match(/^\s*/)[0].length > ind); k += 1) body.push(lines[k]);
|
|
60
|
+
return body.join('\n');
|
|
61
|
+
}
|
|
62
|
+
const rest = lines.slice(i).join('\n');
|
|
63
|
+
const open = rest.indexOf('{');
|
|
64
|
+
if (open < 0) return '';
|
|
65
|
+
let depth = 0;
|
|
66
|
+
for (let k = open; k < rest.length; k += 1) {
|
|
67
|
+
if (rest[k] === '{') depth += 1;
|
|
68
|
+
else if (rest[k] === '}' && --depth === 0) return rest.slice(open, k + 1);
|
|
69
|
+
}
|
|
70
|
+
return rest.slice(open);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const INDEX_MEMBER = [
|
|
74
|
+
/\b(?:int|size_t|std::size_t|unsigned|uint\w*|int\w*_t|short|long)\s+(?:index|idx|ix)\s*[;={]/,
|
|
75
|
+
/\b(?:int|size_t|std::size_t|unsigned|uint\w*|int\w*_t|short|long)\s+\w*(?:Index|Idx|Ix)\s*[;={]/,
|
|
76
|
+
/\b(?:this|self)\.(?:index|idx|ix|\w*(?:Index|Idx|Ix))\s*=/,
|
|
77
|
+
/^\s*(?:index|idx|ix|\w*(?:Index|Idx|Ix))\s*[:=]/m,
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
// The type is made N times, N a named count: an array of it (std::array, T name[N], a vector of N, Array.from, a list comprehension). An
|
|
81
|
+
// array whose name or count holds a known concept word is that concept's (Box slot[4] is per slot, known); a table with a
|
|
82
|
+
// filled-in initializer is data, not instances.
|
|
83
|
+
function madeN(text, name, shape) {
|
|
84
|
+
const N = '([A-Za-z_]\\w*|\\d+)';
|
|
85
|
+
const res = [
|
|
86
|
+
new RegExp(`std::array\\s*<\\s*${name}\\s*,\\s*${N}\\s*>\\s*(\\w*)(\\s*=?\\s*\\{[^}]*)?`, 'g'),
|
|
87
|
+
new RegExp(`\\b${name}\\s+(?<arr>\\w+)\\s*\\[\\s*${N}\\s*\\](\\s*=?\\s*\\{[^}]*)?`, 'g'),
|
|
88
|
+
new RegExp(`std::vector\\s*<\\s*${name}\\s*>\\s*(\\w+)\\s*[({]\\s*${N}\\s*[)}]()`, 'g'),
|
|
89
|
+
new RegExp(`(\\w+)\\s*=\\s*Array\\.from\\(\\s*\\{\\s*length\\s*:\\s*${N}\\s*\\}[^)]*new\\s+${name}\\b()`, 'g'),
|
|
90
|
+
new RegExp(`(\\w+)\\s*=\\s*\\[\\s*${name}\\(.*\\)\\s+for\\s+\\w+\\s+in\\s+range\\(\\s*${N}\\s*\\)()`, 'g'),
|
|
91
|
+
];
|
|
92
|
+
for (const re of res) {
|
|
93
|
+
for (const m of text.matchAll(re)) {
|
|
94
|
+
const [count, arr, init] = re === res[0] ? [m[1], m[2], m[3]] : re === res[1] ? [m[2], m[1], m[3]] : [m[2], m[1], m[3]];
|
|
95
|
+
if (/^\d+$/.test(count)) continue; // a literal size is a capacity or a fixed table; instances are counted by a named count
|
|
96
|
+
if (init && /\{\s*[^}\s]/.test(init)) continue; // a table of values
|
|
97
|
+
if (conceptOfName(arr ?? '', shape) || conceptOfName(count, shape)) continue;
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function findUnknownTypes(text, file, shape) {
|
|
105
|
+
if (!shape || !CODE.test(file ?? '')) return [];
|
|
106
|
+
const python = /\.py$/i.test(file);
|
|
107
|
+
const lines = text.split('\n');
|
|
108
|
+
const out = [];
|
|
109
|
+
lines.forEach((l, i) => {
|
|
110
|
+
const m = python ? l.match(/^\s*class\s+([A-Za-z_]\w*)/) : l.match(/\b(?:class|struct|enum(?:\s+class|\s+struct)?)\s+(?:\[\[\w+\]\]\s+)?(?:alignas\([^)]*\)\s+)?([A-Za-z_]\w*)\s*(?:final\s*)?(?:extends\s+[\w.]+\s*)?(?::[^;{]*)?(?:\{|$)/);
|
|
111
|
+
if (!m || /^\s*(?:\/\/|\*|#)/.test(l) || /\bfriend\b|\btemplate\s*<\s*class\b/.test(l)) return;
|
|
112
|
+
const name = m[1];
|
|
113
|
+
if (conceptOfName(name, shape) || shape.values?.has(name)) return; // a listed value type is not a container (KWB-10)
|
|
114
|
+
const body = bodyOf(lines, i, python);
|
|
115
|
+
const inArray = madeN(text, name, shape);
|
|
116
|
+
if (!INDEX_MEMBER.some((re) => re.test(body)) && !inArray) return;
|
|
117
|
+
out.push({ line: i + 1, text: l.trim(), concept: null, name, kind: 'type', why: `defines ${name}, a container of instances whose concept has no row in ${SHAPE_FILE} — add the row to ${SHAPE_FILE} first (one or many, its generator, its selectors), then build the type` });
|
|
118
|
+
});
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// The definitions an edit adds: names defined after it that were not defined before it.
|
|
123
|
+
export function addedDefs(before, after, find) {
|
|
124
|
+
const had = new Set(find(before ?? '').map((d) => d.name));
|
|
125
|
+
return find(after ?? '').filter((d) => !had.has(d.name));
|
|
126
|
+
}
|