@thedecipherist/claude-packs 0.0.0-stage → 1.1.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 +754 -0
- package/LICENSE +21 -0
- package/README.md +573 -2
- package/bin/claude-packs.mjs +212 -0
- package/configurations/base/config.json +85 -0
- package/configurations/cpp/config.json +93 -0
- package/configurations/full-stack/config.json +153 -0
- package/configurations/go/config.json +98 -0
- package/configurations/node/config.json +102 -0
- package/configurations/python/config.json +99 -0
- package/configurations/rust/config.json +106 -0
- package/configurations/synth-creator/config.json +148 -0
- package/global/hooks/gnu-shell.mjs +131 -0
- package/global/hooks/lib/shell-writes.mjs +2 -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/build-queue.mjs +22 -0
- package/lib/claude-md.mjs +60 -0
- package/lib/config-cli.mjs +36 -0
- package/lib/configs.mjs +145 -0
- package/lib/coverage.mjs +94 -0
- package/lib/custom-packs.mjs +93 -0
- package/lib/doctor.mjs +126 -0
- package/lib/fix-plan.mjs +72 -0
- package/lib/found-folders.mjs +25 -0
- package/lib/git-tokens.mjs +64 -0
- package/lib/global.mjs +125 -0
- package/lib/hook-proof.mjs +45 -0
- package/lib/hook-timing.mjs +42 -0
- package/lib/hover-gate.mjs +29 -0
- package/lib/kit-command.mjs +14 -0
- package/lib/kit-lock.mjs +149 -0
- package/lib/layout.mjs +99 -0
- package/lib/logs.mjs +109 -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 +109 -0
- package/lib/planner.mjs +59 -0
- package/lib/repeats.mjs +65 -0
- package/lib/repo-weight.mjs +28 -0
- package/lib/rule-tests.mjs +42 -0
- package/lib/rules.mjs +40 -0
- package/lib/scan-detect.mjs +130 -0
- package/lib/scan.mjs +171 -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 +31 -0
- package/lib/switches.mjs +44 -0
- package/lib/symmetry-doctor.mjs +26 -0
- package/lib/sync.mjs +133 -0
- package/lib/workorder.mjs +26 -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 +172 -0
- package/packs/audio-core/hooks/realtime-guard.mjs +67 -0
- package/packs/audio-core/pack.json +192 -0
- package/packs/audio-core/rules/a-control-is-proved-through-its-input.md +26 -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 +28 -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 +23 -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 +35 -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 +25 -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 +99 -0
- package/packs/base/hooks/dispatch-once.mjs +124 -0
- package/packs/base/hooks/file-size-guard.mjs +61 -0
- package/packs/base/hooks/folder-law.mjs +77 -0
- package/packs/base/hooks/git-guard.mjs +202 -0
- package/packs/base/hooks/lesson-checks.mjs +182 -0
- package/packs/base/hooks/lib/apply-edit.mjs +20 -0
- package/packs/base/hooks/lib/code-shapes.mjs +142 -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/crash-policy.mjs +47 -0
- package/packs/base/hooks/lib/dup-index.mjs +204 -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 +68 -0
- package/packs/base/hooks/lib/function-shapes.mjs +90 -0
- package/packs/base/hooks/lib/git-adds.mjs +21 -0
- package/packs/base/hooks/lib/git-auth.mjs +44 -0
- package/packs/base/hooks/lib/git-guard-selftest.mjs +192 -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 +69 -0
- package/packs/base/hooks/lib/layout.mjs +58 -0
- package/packs/base/hooks/lib/making-check-selftest.mjs +112 -0
- package/packs/base/hooks/lib/near-copies.mjs +76 -0
- package/packs/base/hooks/lib/program-read.mjs +199 -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/rust-scan.mjs +139 -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-lists.mjs +31 -0
- package/packs/base/hooks/lib/shape-prose.mjs +78 -0
- package/packs/base/hooks/lib/shape.mjs +273 -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 +86 -0
- package/packs/base/hooks/lib/unread-writes.mjs +24 -0
- package/packs/base/hooks/lib/writes.mjs +71 -0
- package/packs/base/hooks/making-check.mjs +248 -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 +140 -0
- package/packs/base/hooks/secrets-guard.mjs +99 -0
- package/packs/base/hooks/shape-law.mjs +140 -0
- package/packs/base/hooks/suite-once.mjs +96 -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 +19 -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 +23 -0
- package/packs/base/rules/unattended-runs.md +15 -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/new-project/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/skills/write-a-workorder/SKILL.md +41 -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 +26 -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 +29 -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/postgres/checks/pg-bind-values.json +33 -0
- package/packs/postgres/checks/pg-concurrently-in-txn.json +18 -0
- package/packs/postgres/checks/pg-fk-not-valid.json +18 -0
- package/packs/postgres/checks/pg-index-concurrently.json +18 -0
- package/packs/postgres/checks/pg-no-float-money.json +19 -0
- package/packs/postgres/checks/pg-no-money.json +18 -0
- package/packs/postgres/checks/pg-no-required-column.json +18 -0
- package/packs/postgres/checks/pg-no-volatile-default.json +18 -0
- package/packs/postgres/checks/pg-not-exists.json +18 -0
- package/packs/postgres/checks/pg-timestamptz.json +18 -0
- package/packs/postgres/hooks/db-wipe-guard.mjs +95 -0
- package/packs/postgres/pack.json +48 -0
- package/packs/postgres/rules/postgres-migrations-never-stop-traffic.md +28 -0
- package/packs/postgres/rules/postgres-one-pool-per-process.md +21 -0
- package/packs/postgres/rules/postgres-queries-are-null-safe-and-bounded.md +26 -0
- package/packs/postgres/rules/postgres-types-say-what-they-hold.md +22 -0
- package/packs/postgres/rules/postgres-values-are-bound.md +130 -0
- package/packs/postgres/skills/postgres-safe-migration/SKILL.md +45 -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 +22 -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 +22 -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/rust/checks/rust-allow-needs-reason.json +16 -0
- package/packs/rust/checks/rust-blocking-in-async.json +32 -0
- package/packs/rust/checks/rust-if-chain-is-a-match.json +32 -0
- package/packs/rust/checks/rust-static-mut.json +32 -0
- package/packs/rust/checks/rust-string-as-error.json +34 -0
- package/packs/rust/checks/rust-stub-left-in.json +32 -0
- package/packs/rust/checks/rust-unsafe-needs-safety.json +31 -0
- package/packs/rust/checks/rust-unwrap-in-library.json +34 -0
- package/packs/rust/checks/rust-wildcard-dependency.json +16 -0
- package/packs/rust/clippy.toml +5 -0
- package/packs/rust/pack.json +27 -0
- package/packs/rust/rules/rust-async-never-blocks.md +20 -0
- package/packs/rust/rules/rust-borrow-before-you-clone.md +19 -0
- package/packs/rust/rules/rust-errors-are-returned.md +22 -0
- package/packs/rust/rules/rust-lints-are-the-gate.md +23 -0
- package/packs/rust/rules/rust-repeats-become-templates.md +28 -0
- package/packs/rust/rules/rust-unsafe-is-proved.md +21 -0
- package/packs/rust/skills/rust-extract-template/SKILL.md +50 -0
- package/packs/rust/skills/rust-port/SKILL.md +22 -0
- package/packs/rust/skills/rust-review/SKILL.md +24 -0
- package/packs/rust/tools/rust-lints.toml +19 -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/bounce-a-plugin/SKILL.md +40 -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/sound-design/tools/vst3host/build.sh +73 -0
- package/packs/sound-design/tools/vst3host/host_events.h +101 -0
- package/packs/sound-design/tools/vst3host/options.h +172 -0
- package/packs/sound-design/tools/vst3host/render.cpp +245 -0
- package/packs/sound-design/tools/vst3host/session.h +241 -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 +175 -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 +21 -0
- package/packs/synth-creator/tools/buildq.mjs +74 -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
- package/prompts/workorder/PROMPT.md +53 -0
- package/prompts/workorder/run.sh +36 -0
- package/prompts/workorder/workorder.md +59 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// A pack.json is a manifest like a package.json (0.19.0: it says stock or custom, has a git url, and who made it). A custom pack comes
|
|
2
|
+
// from somebody else's repository, so these fields are how a project knows what it is running: what it is, who made it, where it lives.
|
|
3
|
+
const TYPES = ['stock', 'custom'];
|
|
4
|
+
const SEMVER = /^\d+\.\d+\.\d+(?:-[\w.]+)?$/;
|
|
5
|
+
const GIT_URL = /^(?:https?:\/\/|ssh:\/\/|file:\/\/|git@)\S+$/;
|
|
6
|
+
|
|
7
|
+
const FIELDS = {
|
|
8
|
+
version: (p) => SEMVER.test(p.version ?? '') || 'needs a version like 1.2.0',
|
|
9
|
+
description: (p) => Boolean(p.description?.trim()) || 'needs a description',
|
|
10
|
+
type: (p) => TYPES.includes(p.type) || `needs a type: ${TYPES.join(' or ')}`,
|
|
11
|
+
author: (p) => Boolean(String(p.author?.name ?? p.author ?? '').trim()) || 'needs an author: who made it',
|
|
12
|
+
license: (p) => Boolean(p.license?.trim()) || 'needs a license',
|
|
13
|
+
repository: (p) => GIT_URL.test(p.repository?.url ?? '') || 'needs repository.url: the git url it lives at',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// The problems of one manifest; `folder` is where it was found, and its name must say the same.
|
|
17
|
+
export function manifestProblems(pack, folder = pack.name) {
|
|
18
|
+
const said = Object.values(FIELDS).map((check) => check(pack)).filter((r) => r !== true).map((r) => `pack ${folder} ${r}`);
|
|
19
|
+
return pack.name === folder ? said : [`pack ${folder} says its name is ${pack.name}`, ...said];
|
|
20
|
+
}
|
package/lib/packs.mjs
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Reads the packs shipped in this npm package and plans which file goes where in a project.
|
|
2
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
3
|
+
import { dirname, join, relative } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
export const PACKS_DIR = join(dirname(fileURLToPath(import.meta.url)), '..', 'packs');
|
|
7
|
+
const CLAUDE_DIRS = ['rules', 'checks', 'hooks', 'tools'];
|
|
8
|
+
// A tool's own config, read from the project's root: the project's once written, so a pack's file never replaces it.
|
|
9
|
+
const PROJECT_ROOT_FILES = [['clang-tidy', '.clang-tidy'], ['clippy.toml', 'clippy.toml']];
|
|
10
|
+
|
|
11
|
+
// Packs from a team's own repository (lib/custom-packs.mjs): name -> the folder it was fetched to.
|
|
12
|
+
const CUSTOM = new Map();
|
|
13
|
+
export const registerPack = (name, folder) => CUSTOM.set(name, folder);
|
|
14
|
+
export const customPacks = () => [...CUSTOM.keys()];
|
|
15
|
+
export const packRoot = (name) => CUSTOM.get(name) ?? join(PACKS_DIR, name);
|
|
16
|
+
|
|
17
|
+
export function availablePacks() {
|
|
18
|
+
return [...readdirSync(PACKS_DIR).filter((d) => existsSync(join(PACKS_DIR, d, 'pack.json'))), ...CUSTOM.keys()];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function readPack(name) {
|
|
22
|
+
const file = join(packRoot(name), 'pack.json');
|
|
23
|
+
if (!existsSync(file)) throw new Error(`unknown pack "${name}" (available: ${availablePacks().join(', ')})`);
|
|
24
|
+
const pack = JSON.parse(readFileSync(file, 'utf8'));
|
|
25
|
+
if (!CUSTOM.has(name)) return pack;
|
|
26
|
+
// A hook runs code on every tool call, so somebody else's hook is off until the project names it in "enable".
|
|
27
|
+
const hooks = join(packRoot(name), 'hooks');
|
|
28
|
+
return { ...pack, optIn: [...(pack.optIn ?? []), ...(existsSync(hooks) ? readdirSync(hooks).filter((f) => /\.(mjs|py)$/.test(f)).map((f) => `hooks/${f}`) : [])] };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Python's byte-code caches are never pack files (a selftest run inside the package makes them).
|
|
32
|
+
const JUNK = /^(__pycache__|\.DS_Store)$|\.pyc$/;
|
|
33
|
+
|
|
34
|
+
function walk(dir) {
|
|
35
|
+
if (!existsSync(dir)) return [];
|
|
36
|
+
return readdirSync(dir).filter((entry) => !JUNK.test(entry)).flatMap((entry) => {
|
|
37
|
+
const abs = join(dir, entry);
|
|
38
|
+
return statSync(abs).isDirectory() ? walk(abs) : [abs];
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Returns [{ src, rel }] where rel is the destination relative to the project root.
|
|
43
|
+
// Skills go to the pack's own folder (lazy-loaded nested skills) when it declares one.
|
|
44
|
+
export function planFiles(name, layout) {
|
|
45
|
+
const pack = readPack(name);
|
|
46
|
+
const root = packRoot(name);
|
|
47
|
+
const plan = [];
|
|
48
|
+
// A rule that states a hook's law ("# hook: <file>" in its frontmatter) goes where its hook goes: held or left out with it (0.10.0).
|
|
49
|
+
const ruleHook = (src) => readFileSync(src, 'utf8').match(/^---\n(?:[^\n]*\n)*?# hook:\s*(\S+)\s*\n(?:[^\n]*\n)*?---/)?.[1];
|
|
50
|
+
const off = (src) => {
|
|
51
|
+
const rel = relative(root, src);
|
|
52
|
+
if (isDisabled(layout, rel)) return true;
|
|
53
|
+
const hook = /^rules[\\/][^\\/]+\.md$/.test(rel) && ruleHook(src);
|
|
54
|
+
return Boolean(hook) && isDisabled(layout, `hooks/${hook}`);
|
|
55
|
+
};
|
|
56
|
+
for (const dir of CLAUDE_DIRS) {
|
|
57
|
+
for (const src of walk(join(root, dir))) if (!off(src)) plan.push({ src, rel: join('.claude', relative(root, src)) });
|
|
58
|
+
}
|
|
59
|
+
const skillFolder = pack.skillsIn && layout.folders[pack.skillsIn];
|
|
60
|
+
const skillBase = skillFolder ? join(skillFolder.path, '.claude') : '.claude';
|
|
61
|
+
for (const src of walk(join(root, 'skills'))) if (!off(src)) plan.push({ src, rel: join(skillBase, relative(root, src)) });
|
|
62
|
+
for (const [file, rel] of PROJECT_ROOT_FILES) if (existsSync(join(root, file))) plan.push({ src: join(root, file), rel, userOwned: true });
|
|
63
|
+
return plan;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Every hook script any pack ships (node .mjs or python3 .py), by file name. settings.json entries
|
|
67
|
+
// that run one of these are the package's; every other hook entry is the project's own.
|
|
68
|
+
export function kitHookScripts() {
|
|
69
|
+
const out = new Set();
|
|
70
|
+
for (const name of availablePacks()) {
|
|
71
|
+
const dir = join(PACKS_DIR, name, 'hooks');
|
|
72
|
+
if (existsSync(dir)) for (const f of readdirSync(dir)) if (/\.(mjs|py)$/.test(f)) out.add(f);
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// layout.disable lists pack items a project switches off because it already has its own:
|
|
78
|
+
// "checks/one-seeded-random.json", "hooks/no-shipped-images.mjs", "rules/x.md", "skills/gpu-capture".
|
|
79
|
+
export function isDisabled(layout, packRel) {
|
|
80
|
+
const p = packRel.replace(/\\/g, '/');
|
|
81
|
+
return (layout.disable ?? []).some((d) => p === d || p.startsWith(`${d.replace(/\/$/, '')}/`));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// A preset names a stack; `init node-api` installs exactly the packs that stack needs (v7 additions 0.11.0, v8 0.12.0). The
|
|
85
|
+
// configuration is then the smallest one that takes those packs (lib/configs.mjs inferConfig): `node` for the node
|
|
86
|
+
// presets, `synth-creator` for synth.
|
|
87
|
+
export const PRESETS = {
|
|
88
|
+
'node-api': ['js-ts', 'http-api', 'mongo', 'fastify', 'ai', 'docker'],
|
|
89
|
+
'node-ui': ['js-ts', 'http-api', 'mongo', 'fastify', 'html-ui', 'stored-text', 'ui-craft', 'docker'],
|
|
90
|
+
'next-app': ['js-ts', 'http-api', 'react', 'mongo', 'web', 'stored-text', 'ui-craft', 'ai', 'docker'],
|
|
91
|
+
'mcp-server': ['js-ts', 'mongo', 'ai', 'shell-hooks'],
|
|
92
|
+
'node-cli': ['js-ts', 'tui'],
|
|
93
|
+
'node-lib': ['js-ts'],
|
|
94
|
+
edge: ['nginx', 'docker'],
|
|
95
|
+
synth: ['cpp', 'audio-core'],
|
|
96
|
+
'synth-rebuild': ['cpp', 'audio-core', 'reverse-engineering', 'midi', 'sound-design', 'instrument-shell'],
|
|
97
|
+
'python-app': ['python', 'shell', 'docker'],
|
|
98
|
+
'shell-tools': ['shell'],
|
|
99
|
+
'go-app': ['go', 'shell', 'docker', 'ci'],
|
|
100
|
+
'rust-app': ['rust', 'shell', 'docker', 'ci'],
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Base always first, no duplicates, presets expanded, in the order given.
|
|
104
|
+
export function normalisePacks(names) {
|
|
105
|
+
const out = ['base'];
|
|
106
|
+
for (const n of names.flatMap((p) => (Object.hasOwn(PRESETS, p) ? PRESETS[p] : [p]))) if (!out.includes(n)) out.push(n);
|
|
107
|
+
for (const n of out) readPack(n);
|
|
108
|
+
return out;
|
|
109
|
+
}
|
package/lib/planner.mjs
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// The planner-skills generator (0.10.0, STEP 3 item 5; KWB-35): one project-layout skill body per configuration, made
|
|
2
|
+
// from that configuration's layout template, its packs and its preamble, for a planner that briefs work before any project
|
|
3
|
+
// of that kind exists (or without reading one). `claude-packs planner-skills` prints them; `--out <dir>` writes
|
|
4
|
+
// <dir>/<configuration>/project-layout/SKILL.md.
|
|
5
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { availableConfigs, itemsOf, planOf, readConfig } from './configs.mjs';
|
|
8
|
+
|
|
9
|
+
export function plannerSkill(name) {
|
|
10
|
+
const c = readConfig(name);
|
|
11
|
+
const folders = c.layout?.folders ?? {};
|
|
12
|
+
const where = (k) => (folders[k.in] ? `${folders[k.in].path}/` : k.in);
|
|
13
|
+
const items = itemsOf(planOf(c, [...c.packs.always, ...c.packs.default]));
|
|
14
|
+
return [
|
|
15
|
+
'---',
|
|
16
|
+
'name: project-layout',
|
|
17
|
+
`description: Use when planning, briefing or placing work in a ${name} project: where a new file, folder or kind of thing goes, and which laws hold there.`,
|
|
18
|
+
'---',
|
|
19
|
+
`# Project layout: ${name}`,
|
|
20
|
+
'',
|
|
21
|
+
c.description,
|
|
22
|
+
'',
|
|
23
|
+
'## Folders',
|
|
24
|
+
'',
|
|
25
|
+
...Object.values(folders).map((f) => `- \`${f.path}/\`: ${f.purpose}`),
|
|
26
|
+
'',
|
|
27
|
+
...(Object.keys(c.layout?.kinds ?? {}).length ? ['## What goes where', '', ...Object.values(c.layout.kinds).map((k) => `- ${k.what} → \`${where(k)}\``), ''] : []),
|
|
28
|
+
'## What it installs',
|
|
29
|
+
'',
|
|
30
|
+
`- packs: ${c.packs.always.join(', ')}${c.packs.choose.length ? `; to choose: ${c.packs.choose.join(', ')}` : ''}`,
|
|
31
|
+
`- hooks: ${items.hooks.join(', ')}`,
|
|
32
|
+
`- skills: ${items.skills.join(', ') || 'none'}`,
|
|
33
|
+
`- rules: ${items.rules.length} (\`npx --no-install @thedecipherist/claude-packs configs\` lists the configurations)`,
|
|
34
|
+
'',
|
|
35
|
+
'## House rules',
|
|
36
|
+
'',
|
|
37
|
+
'<constraints>',
|
|
38
|
+
'Always do:',
|
|
39
|
+
'- Put every kind of thing in its one place (the folders above); a new folder goes into `.claude/layout.json` first.',
|
|
40
|
+
...c.preamble.always.map((l) => `- ${l}`),
|
|
41
|
+
'',
|
|
42
|
+
'Never do:',
|
|
43
|
+
...c.preamble.never.map((l) => `- ${l}`),
|
|
44
|
+
'</constraints>',
|
|
45
|
+
'',
|
|
46
|
+
].join('\n');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function plannerSkills(out = null, log = console.log) {
|
|
50
|
+
for (const name of availableConfigs()) {
|
|
51
|
+
const body = plannerSkill(name);
|
|
52
|
+
if (!out) { log(`==> ${name}/project-layout/SKILL.md\n${body}`); continue; }
|
|
53
|
+
const dir = join(out, name, 'project-layout');
|
|
54
|
+
mkdirSync(dir, { recursive: true });
|
|
55
|
+
writeFileSync(join(dir, 'SKILL.md'), body);
|
|
56
|
+
log(` wrote ${join(dir, 'SKILL.md')}`);
|
|
57
|
+
}
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
package/lib/repeats.mjs
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// What a project repeats, for `scan`: every shape written by hand more than once, the copies of it, and what it should become. A run seen as
|
|
2
|
+
// many overlapping windows is reported once, from its first window.
|
|
3
|
+
import { extname } from 'node:path';
|
|
4
|
+
import { TEMPLATE, TESTS, copiesNeeded, shapesOf, skippedBy } from '../packs/base/hooks/lib/elements.mjs';
|
|
5
|
+
import { parametersOf } from '../packs/base/hooks/lib/code-shapes.mjs';
|
|
6
|
+
import { languageOf } from '../packs/base/hooks/lib/languages.mjs';
|
|
7
|
+
import { definesOf, fingerprint, nearCopy } from '../packs/base/hooks/lib/near-copies.mjs';
|
|
8
|
+
import { matchesAny } from '../packs/base/hooks/lib/glob.mjs';
|
|
9
|
+
|
|
10
|
+
const copiesOf = (group) => new Set(group.map((x) => `${x.rel}:${x.line}`)).size;
|
|
11
|
+
const oneName = (group) => new Set(group.map((x) => x.what)).size === 1;
|
|
12
|
+
const isRepeat = (g) => copiesOf(g) >= copiesNeeded(g[0], oneName(g)) && (g[0].kind !== 'setup' || !sameObject(g));
|
|
13
|
+
// One object set up twice in one file is one object.
|
|
14
|
+
const sameObject = (g) => new Set(g.map((x) => `${x.rel} ${x.what}`)).size < 2;
|
|
15
|
+
|
|
16
|
+
function firstWindows(groups) {
|
|
17
|
+
const seen = new Set(groups.filter((g) => g[0].kind === 'run').map((g) => g.map((x) => `${x.rel}:${x.at}`).join()));
|
|
18
|
+
return groups.filter((g) => g[0].kind !== 'run' || !seen.has(g.map((x) => `${x.rel}:${x.at - 1}`).join()));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function repeatedShapes(texts, layout = {}) {
|
|
22
|
+
const by = new Map();
|
|
23
|
+
for (const [rel, text] of texts) {
|
|
24
|
+
if (matchesAny(rel, [...TESTS, ...skippedBy(layout)])) continue;
|
|
25
|
+
for (const s of shapesOf(text, extname(rel))) { if (!by.has(s.id)) by.set(s.id, []); by.get(s.id).push({ ...s, rel }); }
|
|
26
|
+
}
|
|
27
|
+
const source = new Map(texts);
|
|
28
|
+
return firstWindows([...by.values()].filter(isRepeat)).sort((a, b) => b.length * b[0].nodes - a.length * a[0].nodes).map((g) => {
|
|
29
|
+
const [a, b] = g;
|
|
30
|
+
return { kind: g[0].kind, what: g[0].what, copies: g.length, at: g.map((x) => `${x.rel}:${x.line}`), template: TEMPLATE[g[0].kind],
|
|
31
|
+
parameters: g[0].kind === 'run' ? parametersOf(a.id, source.get(a.rel), source.get(b.rel), extname(a.rel)).slice(0, 6) : [] };
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const repeatLine = (r) => ` - ${r.copies}x ${r.what}: ${r.at.slice(0, 4).join(', ')}${r.copies > 4 ? ', ...' : ''}. Make it ${r.template}${r.parameters.length ? ` (differs in: ${r.parameters.join(', ').replace(/`/g, "'")})` : ''}`;
|
|
36
|
+
|
|
37
|
+
const sourceTexts = (texts, layout) => texts.filter(([rel]) => languageOf(extname(rel)) && !matchesAny(rel, [...TESTS, ...skippedBy(layout)]));
|
|
38
|
+
|
|
39
|
+
// Files that hold most of another, in order, with other names. A pair is reported once.
|
|
40
|
+
export function nearCopies(texts, layout = {}) {
|
|
41
|
+
const all = sourceTexts(texts, layout).map(([file, text]) => ({ file, st: fingerprint(text, extname(file)), lang: languageOf(extname(file)) }));
|
|
42
|
+
const seen = new Set();
|
|
43
|
+
const out = [];
|
|
44
|
+
for (const x of all) {
|
|
45
|
+
const hit = nearCopy(x.st, all.filter((o) => o !== x && o.lang === x.lang));
|
|
46
|
+
if (!hit || seen.has(`${hit.file} ${x.file}`)) continue;
|
|
47
|
+
seen.add(`${x.file} ${hit.file}`);
|
|
48
|
+
out.push({ file: x.file, line: hit.line, other: hit.file, from: hit.from, to: hit.to, matched: hit.matched, of: hit.of });
|
|
49
|
+
}
|
|
50
|
+
return out.sort((a, b) => b.matched - a.matched);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Compound names defined in two or three files: a helper made again. More files than that is a convention.
|
|
54
|
+
export function remadeNames(texts, layout = {}) {
|
|
55
|
+
const by = new Map();
|
|
56
|
+
for (const [rel, text] of sourceTexts(texts, layout)) {
|
|
57
|
+
for (const [name, line] of definesOf(text, extname(rel))) {
|
|
58
|
+
if (!/[a-z0-9][A-Z]|[a-z0-9]_[a-z0-9]/.test(name)) continue;
|
|
59
|
+
const key = `${languageOf(extname(rel)).name} ${name}`;
|
|
60
|
+
if (!by.has(key)) by.set(key, { name, at: [] });
|
|
61
|
+
by.get(key).at.push(`${rel}:${line}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return [...by.values()].filter((m) => m.at.length >= 2 && m.at.length <= 3 && new Set(m.at.map((a) => a.split(':')[0])).size >= 2);
|
|
65
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// What a repo carries that is not source: its tracked size, files too big for git, and working screenshots in its root. For `scan`; the `repo-litter`
|
|
2
|
+
// hook stops new ones at `git add`.
|
|
3
|
+
import { statSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { BIG_MB, isRootImage } from '../packs/base/hooks/repo-litter.mjs';
|
|
6
|
+
|
|
7
|
+
const MB = 1024 * 1024;
|
|
8
|
+
|
|
9
|
+
// A build folder is known by what the build leaves in it, whatever it was called: `build-reso/` held 43% of one repo's tracked files.
|
|
10
|
+
const BUILD_MARK = /^(.*?\/)?(?:CMakeFiles\/|CMakeCache\.txt$)/;
|
|
11
|
+
export function buildFolders(files) {
|
|
12
|
+
const folders = new Set(files.map((f) => f.match(BUILD_MARK)).filter((m) => m?.[1]).map((m) => m[1]));
|
|
13
|
+
return [...folders].filter((d) => ![...folders].some((o) => o !== d && d.startsWith(o)));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function repoWeight(dir, files) {
|
|
17
|
+
const sized = files.flatMap((file) => { try { return [{ file, bytes: statSync(join(dir, file)).size }]; } catch { return []; } });
|
|
18
|
+
const total = sized.reduce((n, f) => n + f.bytes, 0);
|
|
19
|
+
const big = sized.filter((f) => f.bytes > BIG_MB * MB).sort((a, b) => b.bytes - a.bytes).map((f) => ({ file: f.file, mb: Math.round(f.bytes / MB) }));
|
|
20
|
+
const built = buildFolders(files).map((folder) => ({ folder, files: files.filter((f) => f.startsWith(folder)).length }));
|
|
21
|
+
return { mb: Math.round(total / MB), big, rootImages: files.filter((f) => isRootImage(f, [])), built, files: files.length };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const weightLines = (w) => [
|
|
25
|
+
`- ${w.mb} MB tracked; ${w.big.length} file(s) over ${BIG_MB} MB${w.big.length ? `: ${w.big.slice(0, 5).map((b) => `${b.file} (${b.mb} MB)`).join(', ')}` : ''}`,
|
|
26
|
+
...w.built.map((b) => `- build output held in git: ${b.folder} (${b.files} of ${w.files} files), left out of this scan`),
|
|
27
|
+
...(w.rootImages.length ? [`- ${w.rootImages.length} image(s) in the repo root, which look like working screenshots: ${w.rootImages.slice(0, 5).join(', ')}`] : []),
|
|
28
|
+
];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// KF-31: a rule's Check can name a test, and an install reads as if the test exists. Three rules of the MPX 500 named tests that were never written, and
|
|
2
|
+
// the gap held four real bugs while every hook, scan and doctor stayed quiet. doctor is red for a live rule (one that matches a file of the project)
|
|
3
|
+
// whose Check names a test nothing answers to. A test answers by naming the rule (`proves: <rule>`, in a test, bench or gate file), or layout.json
|
|
4
|
+
// "ruleTests" points the rule at a file, or declines it with "none: <reason>".
|
|
5
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
6
|
+
import { basename, join } from 'node:path';
|
|
7
|
+
|
|
8
|
+
const TEST_PLACE = /(^|\/)(tests?|specs?|e2e|bench|benches|benchmarks?|gates?)(\/|$)|(^|[._-])(test|spec)[._-][^/]*$|_test\.[^/]+$/i;
|
|
9
|
+
const MAX_BYTES = 2 * 1024 * 1024;
|
|
10
|
+
const checkOf = (text) => text.split(/^## Check[ \t]*$/m)[1]?.split(/^## /m)[0] ?? '';
|
|
11
|
+
|
|
12
|
+
// "Test:", "Tests:", "- Unit test:", "ThreadSanitizer stress test:", "Benchmark test, offline render:" at the head of a line of the Check.
|
|
13
|
+
export const namesATest = (text) => /^[ \t]*(?:(?:[-*]|\d+\.)[ \t]+)?(?:[\w'-]+[ \t]+){0,3}tests?(?:,[^:\n]*)?:/im.test(checkOf(text));
|
|
14
|
+
|
|
15
|
+
// A file's text, or '' for one too big to be a test or gone since it was listed.
|
|
16
|
+
const smallFileText = (file) => { try { return statSync(file).size > MAX_BYTES ? '' : readFileSync(file, 'utf8'); } catch { return ''; } };
|
|
17
|
+
const inTestFolder = (rel, folders) => TEST_PLACE.test(rel) || folders.some((f) => rel === f || rel.startsWith(`${f}/`));
|
|
18
|
+
|
|
19
|
+
export function ruleTestProblems(projectDir, layout, { liveRules, files }) {
|
|
20
|
+
const dir = join(projectDir, '.claude', 'rules');
|
|
21
|
+
if (!existsSync(dir)) return [];
|
|
22
|
+
const named = readdirSync(dir).filter((f) => liveRules.includes(f) && namesATest(smallFileText(join(dir, f)))).map((f) => basename(f, '.md'));
|
|
23
|
+
if (!named.length) return [];
|
|
24
|
+
const folders = (layout.concepts?.tests ?? []).map((f) => layout.folders?.[f]?.path ?? f).filter((f) => !f.includes('*'));
|
|
25
|
+
const tests = files.filter((f) => inTestFolder(f, folders)).map((f) => smallFileText(join(projectDir, f)));
|
|
26
|
+
const declared = layout.ruleTests ?? {};
|
|
27
|
+
const problems = [];
|
|
28
|
+
const open = named.filter((rule) => {
|
|
29
|
+
const said = declared[rule];
|
|
30
|
+
if (/^none:\s*\S/.test(said ?? '')) return false;
|
|
31
|
+
if (said && said !== 'none') {
|
|
32
|
+
if (!existsSync(join(projectDir, said))) problems.push(`${rule}: ruleTests names ${said}, which does not exist`);
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return !tests.some((t) => t.includes(rule));
|
|
36
|
+
});
|
|
37
|
+
if (open.length) {
|
|
38
|
+
const how = 'Write the test its Check names, and put `proves: <rule>` in it. Or set layout.json "ruleTests": { "<rule>": "<path>" }, or "none: <reason>".';
|
|
39
|
+
problems.push(`${open.length} rule(s) name a test nothing in this project answers to: ${open.join(', ')}\n ${how}`);
|
|
40
|
+
}
|
|
41
|
+
return problems;
|
|
42
|
+
}
|
package/lib/rules.mjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Regenerates every rule's `paths:` from its `# concepts:` line and the layout.
|
|
2
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { globsForConcepts as kitGlobs } from '../packs/base/hooks/lib/layout.mjs';
|
|
6
|
+
|
|
7
|
+
// A rule's text with its `paths:` written from its concepts and the layout; null for a rule
|
|
8
|
+
// without a `# concepts:` line, or (unless strict, which throws) whose concepts resolve to nothing.
|
|
9
|
+
export function renderRule(text, layout, globsForConcepts = kitGlobs, strict = false) {
|
|
10
|
+
const fm = text.match(/^---\n([\s\S]*?)\n---/);
|
|
11
|
+
const line = fm?.[1].match(/^# concepts:\s*(.+)$/m);
|
|
12
|
+
if (!fm || !line) return null;
|
|
13
|
+
const concepts = line[1].split(',').map((s) => s.trim()).filter(Boolean);
|
|
14
|
+
let globs;
|
|
15
|
+
try { globs = globsForConcepts(layout, concepts); } catch (e) { if (strict) throw e; return null; }
|
|
16
|
+
const head = ['---', `# concepts: ${concepts.join(', ')}`, 'paths:', ...globs.map((g) => ` - "${g}"`), '---'].join('\n');
|
|
17
|
+
return text.replace(fm[0], head);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function layoutLib(projectDir) {
|
|
21
|
+
return import(pathToFileURL(join(projectDir, '.claude', 'hooks', 'lib', 'layout.mjs')).href);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function applyPaths(projectDir, { checkOnly = false, skip = new Set() } = {}) {
|
|
25
|
+
const { readLayout, globsForConcepts } = await layoutLib(projectDir);
|
|
26
|
+
const layout = readLayout(projectDir);
|
|
27
|
+
const dir = join(projectDir, '.claude', 'rules');
|
|
28
|
+
if (!existsSync(dir)) return { stale: 0 };
|
|
29
|
+
let stale = 0;
|
|
30
|
+
for (const file of readdirSync(dir).filter((f) => f.endsWith('.md'))) {
|
|
31
|
+
if (skip.has(join('.claude', 'rules', file))) continue; // the user's file or a rule they edited
|
|
32
|
+
const text = readFileSync(join(dir, file), 'utf8');
|
|
33
|
+
const next = renderRule(text, layout, globsForConcepts, true);
|
|
34
|
+
if (next !== null && next !== text) {
|
|
35
|
+
stale += 1;
|
|
36
|
+
if (!checkOnly) writeFileSync(join(dir, file), next);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { stale };
|
|
40
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// What a project is, from its files and its dependencies: the configuration and the packs `scan` recommends, each with its evidence.
|
|
2
|
+
// One row per pack: a pack is recommended when its row finds something, and says what it found.
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { isContent } from '../packs/base/hooks/lib/content.mjs';
|
|
6
|
+
import { readConfig } from './configs.mjs';
|
|
7
|
+
|
|
8
|
+
const dep = (ctx, ...names) => names.find((n) => ctx.deps.has(n) || [...ctx.deps].some((d) => d.startsWith(`${n}/`)));
|
|
9
|
+
const file = (ctx, re) => ctx.files.find((f) => re.test(f));
|
|
10
|
+
const said = (hit, what) => (hit ? `${what}: ${hit}` : null);
|
|
11
|
+
// The first of a project's files with this name that holds this text: a name says too little where every project has the same files.
|
|
12
|
+
const MOST_READ = 40;
|
|
13
|
+
function holds(ctx, name, text) {
|
|
14
|
+
return ctx.files.filter((f) => name.test(f)).slice(0, MOST_READ).find((f) => { try { return text.test(readFileSync(join(ctx.dir, f), 'utf8')); } catch { return false; } });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// A project at its start has no source, and only its notes say what it will be (KF-3: a JUCE plugin's first `suggest` said base, with no warning). Once code
|
|
18
|
+
// exists the code decides, and a word in a README adds nothing.
|
|
19
|
+
const NOTES = /(^|\/)(?:README|PROMPT|BRIEF|SPEC|PLAN)[^/]*\.md$|^docs\/(?:[^/]+\/)*[^/]+\.md$/i;
|
|
20
|
+
const NOTED = {
|
|
21
|
+
'audio-core': /\bJUCE\b|\bVST3?\b|\baudio plug-?in\b/i,
|
|
22
|
+
cpp: /\bC\+\+/,
|
|
23
|
+
midi: /\bMIDI\b/,
|
|
24
|
+
'sound-design': /\b(?:reverb|synth|synthesi[sz]er|oscillator)\b/i,
|
|
25
|
+
'instrument-shell': /\b(?:front panel|plug-?in editor)\b/i,
|
|
26
|
+
'reverse-engineering': /\bfirmware\b|\breverse[- ]engineer/i,
|
|
27
|
+
python: /\bPython\b/,
|
|
28
|
+
rust: /\bRust\b/,
|
|
29
|
+
};
|
|
30
|
+
function noted(ctx, pack) {
|
|
31
|
+
if (!ctx.early || !NOTED[pack]) return null;
|
|
32
|
+
const notes = [...ctx.files, ...ctx.published].filter((f) => NOTES.test(f)).slice(0, MOST_READ);
|
|
33
|
+
const hit = notes.find((f) => { try { return NOTED[pack].test(readFileSync(join(ctx.dir, f), 'utf8')); } catch { return false; } });
|
|
34
|
+
return said(hit, 'named in the notes');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// A driver alone says Postgres; a tool that speaks several databases says it only with this second signal in its own file.
|
|
38
|
+
const POSTGRES_FILES = [
|
|
39
|
+
[/(^|\/)(?:requirements[^/]*\.txt|pyproject\.toml|Pipfile)$/, /\b(?:psycopg2?(?:-binary)?|asyncpg)\b/],
|
|
40
|
+
[/(^|\/)go\.mod$/, /jackc\/pgx|lib\/pq\b|gorm\.io\/driver\/postgres/],
|
|
41
|
+
[/(^|\/)Cargo\.toml$/, /\b(?:tokio-postgres|deadpool-postgres)\b|\b(?:sqlx|diesel|sea-orm)\b[^\n]*postgres/],
|
|
42
|
+
[/(^|\/)Gemfile$/, /^\s*gem\s+['"]pg['"]/m],
|
|
43
|
+
[/\.prisma$/, /provider\s*=\s*"postgresql"/],
|
|
44
|
+
[/(^|\/)drizzle\.config\.[cm]?[jt]s$/, /dialect:\s*['"]postgresql['"]/],
|
|
45
|
+
[/(^|\/)[\w.-]*compose[\w.-]*\.ya?ml$/, /image:\s*['"]?[\w./-]*(?:postgres|postgis|pgvector|timescale)/],
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const ROWS = {
|
|
49
|
+
'js-ts': (c) => said(file(c, /\.(ts|tsx|js|jsx|mjs|cjs)$/), 'JS or TS source'),
|
|
50
|
+
mongo: (c) => said(dep(c, 'mongodb', 'mongoose'), 'dependency'),
|
|
51
|
+
postgres: (c) => said(dep(c, 'pg', 'postgres', 'pg-promise', '@neondatabase/serverless', '@vercel/postgres'), 'dependency')
|
|
52
|
+
?? said(POSTGRES_FILES.map(([name, text]) => holds(c, name, text)).find(Boolean), 'Postgres named in'),
|
|
53
|
+
fastify: (c) => said(dep(c, 'fastify'), 'dependency'),
|
|
54
|
+
ai: (c) => said(dep(c, '@anthropic-ai/sdk', 'openai', 'ai', '@ai-sdk'), 'dependency'),
|
|
55
|
+
docker: (c) => said(file(c, /(^|\/)(Dockerfile[^/]*|[\w.-]*compose[\w.-]*\.ya?ml)$/), 'file'),
|
|
56
|
+
'shell-hooks': (c) => said(file(c, /(^|\/)hooks\/[^/]+\.(sh|mjs|js|cjs)$/), 'hook file'),
|
|
57
|
+
'html-ui': (c) => said(file(c, /(^|\/)(ui|views|templates)\/[^/]+\.ts$|\.view\.ts$/), 'server-rendered UI'),
|
|
58
|
+
web: (c) => said(dep(c, 'next', 'react', 'vue', 'svelte') ?? file(c, /\.(tsx|jsx|vue|svelte|html)$/), 'web UI'),
|
|
59
|
+
tui: (c) => said(dep(c, 'ink', 'blessed', 'terminal-kit'), 'dependency'),
|
|
60
|
+
nginx: (c) => said(file(c, /(^|\/)nginx[^/]*\.conf(?:\.template)?$|\/nginx\/.+\.conf$/), 'file'),
|
|
61
|
+
'stored-text': (c) => (ROWS.web(c) || ROWS['html-ui'](c)) && (ROWS.mongo(c) || dep(c, 'pg', 'mysql2', 'sqlite3', 'better-sqlite3', 'prisma', 'drizzle-orm')) ? 'a UI and a database' : null,
|
|
62
|
+
'ui-craft': (c) => ROWS.web(c) ?? ROWS['html-ui'](c),
|
|
63
|
+
cpp: (c) => said(file(c, /\.(?:cc|cpp|cxx|hh|hpp|mm)$/), 'C++ source'),
|
|
64
|
+
python: (c) => said(file(c, /\.py$/), 'Python source'),
|
|
65
|
+
go: (c) => said(file(c, /\.go$/), 'Go source'),
|
|
66
|
+
rust: (c) => said(file(c, /(^|\/)Cargo\.toml$/) ?? file(c, /\.rs$/), 'Rust crate'),
|
|
67
|
+
midi: (c) => said(file(c, /\.(syx|mid)$|(^|\/)\w*(Midi|MIDI|Sysex|SysEx)\w*\.(h|hpp|cpp|cc)$/), 'MIDI file')
|
|
68
|
+
?? said(holds(c, /\.(h|hpp|cpp|cc)$/, /\b(MidiMessage|isController|isNoteOn|getSysExData)\b/), 'MIDI handling'),
|
|
69
|
+
'sound-design': (c) => said(file(c, /(^|\/)\w*(Voice|Oscillator|Filter|Reverb|Envelope|Lfo|LFO)\w*\.(h|hpp|cpp|cc)$/), 'DSP source'),
|
|
70
|
+
'reverse-engineering': (c) => said(file(c, /(^|\/)[\w-]*(firmware|disasm|disassembl|x86dis|dis8051|reverse[-_]engineer)[\w-]*\.(md|py)$|(^|\/)[\w.-]+[-_]re\.md$|(^|\/)tools\/re\//i), 'reverse-engineering notes')
|
|
71
|
+
?? said(holds(c, /(^|\/)(research|laws|provenance)\.md$/, /\[FW\]/), 'firmware findings'),
|
|
72
|
+
'instrument-shell': (c) => said(file(c, /(^|\/)PluginEditor\.(h|cpp)$/), 'plugin editor'),
|
|
73
|
+
'content-site': (c) => said(c.published.find((f) => /\.(md|mdx|html)$/.test(f)) ?? file(c, /(^|\/)sitemap\.xml$/), 'published page'),
|
|
74
|
+
'dynamic-data': (c) => said(file(c, /(^|[/_-])(cms|entity[-_]?definitions?|field[-_]?configs?|page[-_]?builder|admin[-_]?generator)([/_.-]|$)/i), 'definition-driven file'),
|
|
75
|
+
ci: (c) => said(file(c, /^\.github\/workflows\/[^/]+\.ya?ml$/), 'workflow'),
|
|
76
|
+
'http-api': (c) => said(dep(c, 'express', 'fastify', 'koa', 'hono', '@nestjs/core', 'next'), 'dependency') ?? said(file(c, /(^|\/)app\/api\/.*route\.ts$|(^|\/)routes\/[^/]+\.[jt]s$/), 'route file'),
|
|
77
|
+
react: (c) => said(dep(c, 'react'), 'dependency') ?? said(file(c, /\.[jt]sx$/), 'JSX source'),
|
|
78
|
+
websocket: (c) => said(dep(c, 'ws', 'socket.io', 'uWebSockets.js', '@fastify/websocket'), 'dependency'),
|
|
79
|
+
'multi-tenant': (c) => said(file(c, /(^|[/_-])(tenants?|compan(?:y|ies)|organi[sz]ations?|workspaces?)([/_.-]|$)/i), 'tenant file'),
|
|
80
|
+
'host-agent': (c) => said(dep(c, 'ssh2'), 'dependency') ?? said(file(c, /(^|\/)install[-_]agent|\.service$/), 'agent file'),
|
|
81
|
+
shell: (c) => said(file(c, /\.(?:sh|bash|zsh)$/), 'shell script'),
|
|
82
|
+
'audio-core': (c) => said(file(c, /(^|\/)(JuceHeader\.h|.*\.jucer)$/) ?? file(c, /PluginProcessor\.(h|cpp)$/), 'JUCE file'),
|
|
83
|
+
'gpu-painter': (c) => said(file(c, /\.wgsl$/), 'shader'),
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// The configuration of the project: a synth; else the language most of its files are in; else base. full-stack is offered, never assumed: it fixes a folder layout.
|
|
87
|
+
const LEADS = [['cpp', /\.(?:c|cc|cpp|cxx|h|hh|hpp|mm)$/, 'C++ sources'], ['node', /\.(?:ts|tsx|js|jsx|mjs|cjs)$/, 'JS or TS sources'], ['python', /\.py$/, 'Python sources'], ['go', /\.go$/, 'Go sources'], ['rust', /\.rs$|(^|\/)Cargo\.toml$/, 'Rust sources']];
|
|
88
|
+
|
|
89
|
+
function configFor(ctx) {
|
|
90
|
+
if (ROWS['audio-core'](ctx)) return { name: 'synth-creator', why: 'JUCE sources' };
|
|
91
|
+
if (noted(ctx, 'audio-core')) return { name: 'synth-creator', why: 'named in the notes' };
|
|
92
|
+
const [lead] = LEADS.map(([name, re, why]) => ({ name, why, n: ctx.files.filter((f) => re.test(f)).length })).sort((a, b) => b.n - a.n);
|
|
93
|
+
return lead.n ? lead : { name: 'base', why: 'no stack the kit has a configuration for' };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const INSTALLED = /(^|\/)(\.claude|node_modules)\//;
|
|
97
|
+
|
|
98
|
+
// A project that names its configuration has decided; reading the files again can only disagree with it.
|
|
99
|
+
function namedConfig(projectDir) {
|
|
100
|
+
try {
|
|
101
|
+
const name = JSON.parse(readFileSync(join(projectDir, '.claude/layout.json'), 'utf8')).configuration;
|
|
102
|
+
return name && readConfig(name) ? { name, why: 'named in its layout.json' } : null;
|
|
103
|
+
} catch { return null; }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function recommend(projectDir, files) {
|
|
107
|
+
const deps = new Set();
|
|
108
|
+
for (const f of files.filter((x) => /(^|\/)package\.json$/.test(x))) {
|
|
109
|
+
const p = join(projectDir, f);
|
|
110
|
+
if (!existsSync(p)) continue;
|
|
111
|
+
try {
|
|
112
|
+
const j = JSON.parse(readFileSync(p, 'utf8'));
|
|
113
|
+
for (const k of ['dependencies', 'devDependencies', 'peerDependencies']) for (const n of Object.keys(j[k] ?? {})) deps.add(n);
|
|
114
|
+
} catch { /* a package.json that does not parse says nothing */ }
|
|
115
|
+
}
|
|
116
|
+
const own = files.filter((f) => !INSTALLED.test(f)); // the kit's own files and installed dependencies say nothing about the project
|
|
117
|
+
const ctx = { deps, dir: projectDir, files: own.filter((f) => !isContent(f)), published: own.filter(isContent) };
|
|
118
|
+
ctx.early = !LEADS.some(([, re]) => ctx.files.some((f) => re.test(f)));
|
|
119
|
+
const { name, why } = namedConfig(projectDir) ?? configFor(ctx);
|
|
120
|
+
const config = readConfig(name);
|
|
121
|
+
const packs = [];
|
|
122
|
+
const leftOut = [];
|
|
123
|
+
for (const pack of config.packs.choose) {
|
|
124
|
+
const found = ROWS[pack]?.(ctx) ?? noted(ctx, pack);
|
|
125
|
+
if (found) packs.push({ pack, why: found });
|
|
126
|
+
else leftOut.push(pack);
|
|
127
|
+
}
|
|
128
|
+
const fullStack = dep(ctx, 'next') && dep(ctx, 'fastify');
|
|
129
|
+
return { config: name, why, always: config.packs.always, packs, leftOut, early: ctx.early, fullStack: Boolean(fullStack) };
|
|
130
|
+
}
|