@ketecode/loom 0.0.0-stage → 0.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/LICENSE +21 -0
- package/README.md +516 -2
- package/agents/accessibility-reviewer/AGENT.md +70 -0
- package/agents/accessibility-reviewer/evals/triggers.json +15 -0
- package/agents/agent-evaluator/AGENT.md +74 -0
- package/agents/agent-evaluator/evals/triggers.json +15 -0
- package/agents/architect/AGENT.md +82 -0
- package/agents/architect/evals/triggers.json +17 -0
- package/agents/billing-reviewer/AGENT.md +68 -0
- package/agents/billing-reviewer/evals/triggers.json +16 -0
- package/agents/code-reviewer/AGENT.md +51 -0
- package/agents/code-reviewer/evals/triggers.json +14 -0
- package/agents/code-simplifier/AGENT.md +74 -0
- package/agents/code-simplifier/evals/triggers.json +15 -0
- package/agents/cpp-build-resolver/AGENT.md +73 -0
- package/agents/cpp-build-resolver/evals/triggers.json +16 -0
- package/agents/cpp-reviewer/AGENT.md +59 -0
- package/agents/cpp-reviewer/evals/triggers.json +16 -0
- package/agents/csharp-build-resolver/AGENT.md +84 -0
- package/agents/csharp-build-resolver/evals/triggers.json +17 -0
- package/agents/csharp-reviewer/AGENT.md +88 -0
- package/agents/csharp-reviewer/evals/triggers.json +17 -0
- package/agents/dart-build-resolver/AGENT.md +80 -0
- package/agents/dart-build-resolver/evals/triggers.json +17 -0
- package/agents/dart-reviewer/AGENT.md +66 -0
- package/agents/dart-reviewer/evals/triggers.json +17 -0
- package/agents/database-reviewer/AGENT.md +72 -0
- package/agents/database-reviewer/evals/triggers.json +16 -0
- package/agents/django-reviewer/AGENT.md +61 -0
- package/agents/django-reviewer/evals/triggers.json +15 -0
- package/agents/doc-updater/AGENT.md +92 -0
- package/agents/doc-updater/evals/triggers.json +14 -0
- package/agents/e2e-runner/AGENT.md +88 -0
- package/agents/e2e-runner/evals/triggers.json +16 -0
- package/agents/fastapi-reviewer/AGENT.md +63 -0
- package/agents/fastapi-reviewer/evals/triggers.json +15 -0
- package/agents/flutter-reviewer/AGENT.md +70 -0
- package/agents/flutter-reviewer/evals/triggers.json +15 -0
- package/agents/fsharp-build-resolver/AGENT.md +81 -0
- package/agents/fsharp-build-resolver/evals/triggers.json +15 -0
- package/agents/fsharp-reviewer/AGENT.md +77 -0
- package/agents/fsharp-reviewer/evals/triggers.json +15 -0
- package/agents/go-build-resolver/AGENT.md +85 -0
- package/agents/go-build-resolver/evals/triggers.json +16 -0
- package/agents/go-reviewer/AGENT.md +66 -0
- package/agents/go-reviewer/evals/triggers.json +16 -0
- package/agents/healthcare-reviewer/AGENT.md +65 -0
- package/agents/healthcare-reviewer/evals/triggers.json +16 -0
- package/agents/i18n-reviewer/AGENT.md +64 -0
- package/agents/i18n-reviewer/evals/triggers.json +16 -0
- package/agents/infra-reviewer/AGENT.md +93 -0
- package/agents/infra-reviewer/evals/triggers.json +15 -0
- package/agents/java-build-resolver/AGENT.md +82 -0
- package/agents/java-build-resolver/evals/triggers.json +16 -0
- package/agents/java-reviewer/AGENT.md +64 -0
- package/agents/java-reviewer/evals/triggers.json +16 -0
- package/agents/kotlin-build-resolver/AGENT.md +89 -0
- package/agents/kotlin-build-resolver/evals/triggers.json +16 -0
- package/agents/kotlin-reviewer/AGENT.md +75 -0
- package/agents/kotlin-reviewer/evals/triggers.json +16 -0
- package/agents/mle-reviewer/AGENT.md +68 -0
- package/agents/mle-reviewer/evals/triggers.json +16 -0
- package/agents/network-config-reviewer/AGENT.md +68 -0
- package/agents/network-config-reviewer/evals/triggers.json +17 -0
- package/agents/payments-reviewer/AGENT.md +64 -0
- package/agents/payments-reviewer/evals/triggers.json +16 -0
- package/agents/performance-optimizer/AGENT.md +86 -0
- package/agents/performance-optimizer/evals/triggers.json +16 -0
- package/agents/perl-build-resolver/AGENT.md +84 -0
- package/agents/perl-build-resolver/evals/triggers.json +15 -0
- package/agents/perl-reviewer/AGENT.md +77 -0
- package/agents/perl-reviewer/evals/triggers.json +15 -0
- package/agents/php-build-resolver/AGENT.md +88 -0
- package/agents/php-build-resolver/evals/triggers.json +16 -0
- package/agents/php-reviewer/AGENT.md +61 -0
- package/agents/php-reviewer/evals/triggers.json +15 -0
- package/agents/planner/AGENT.md +75 -0
- package/agents/planner/evals/triggers.json +17 -0
- package/agents/pr-test-analyzer/AGENT.md +82 -0
- package/agents/pr-test-analyzer/evals/triggers.json +16 -0
- package/agents/python-build-resolver/AGENT.md +87 -0
- package/agents/python-build-resolver/evals/triggers.json +16 -0
- package/agents/python-reviewer/AGENT.md +63 -0
- package/agents/python-reviewer/evals/triggers.json +15 -0
- package/agents/rag-reviewer/AGENT.md +71 -0
- package/agents/rag-reviewer/evals/triggers.json +16 -0
- package/agents/react-reviewer/AGENT.md +70 -0
- package/agents/react-reviewer/evals/triggers.json +16 -0
- package/agents/refactor-cleaner/AGENT.md +82 -0
- package/agents/refactor-cleaner/evals/triggers.json +16 -0
- package/agents/ruby-build-resolver/AGENT.md +93 -0
- package/agents/ruby-build-resolver/evals/triggers.json +16 -0
- package/agents/ruby-reviewer/AGENT.md +62 -0
- package/agents/ruby-reviewer/evals/triggers.json +16 -0
- package/agents/rust-build-resolver/AGENT.md +98 -0
- package/agents/rust-build-resolver/evals/triggers.json +16 -0
- package/agents/rust-reviewer/AGENT.md +78 -0
- package/agents/rust-reviewer/evals/triggers.json +15 -0
- package/agents/security-reviewer/AGENT.md +84 -0
- package/agents/security-reviewer/evals/triggers.json +16 -0
- package/agents/silent-failure-hunter/AGENT.md +66 -0
- package/agents/silent-failure-hunter/evals/triggers.json +16 -0
- package/agents/solidity-reviewer/AGENT.md +70 -0
- package/agents/solidity-reviewer/evals/triggers.json +16 -0
- package/agents/swift-build-resolver/AGENT.md +85 -0
- package/agents/swift-build-resolver/evals/triggers.json +15 -0
- package/agents/swift-reviewer/AGENT.md +75 -0
- package/agents/swift-reviewer/evals/triggers.json +15 -0
- package/agents/typescript-build-resolver/AGENT.md +76 -0
- package/agents/typescript-build-resolver/evals/triggers.json +15 -0
- package/agents/typescript-reviewer/AGENT.md +76 -0
- package/agents/typescript-reviewer/evals/triggers.json +15 -0
- package/agents/vue-reviewer/AGENT.md +75 -0
- package/agents/vue-reviewer/evals/triggers.json +15 -0
- package/bin/kete-loom.js +683 -0
- package/commands/aside.md +19 -0
- package/commands/build-fix.md +26 -0
- package/commands/campaign.md +19 -0
- package/commands/changelog.md +36 -0
- package/commands/checkpoint.md +26 -0
- package/commands/codemap.md +25 -0
- package/commands/commit.md +26 -0
- package/commands/deps-audit.md +32 -0
- package/commands/epic-decompose.md +21 -0
- package/commands/explain.md +37 -0
- package/commands/feature-dev.md +22 -0
- package/commands/hookify.md +23 -0
- package/commands/i18n-check.md +39 -0
- package/commands/issue-sync.md +22 -0
- package/commands/jira.md +19 -0
- package/commands/learn.md +23 -0
- package/commands/loom.md +21 -0
- package/commands/loop.md +20 -0
- package/commands/model-route.md +27 -0
- package/commands/onboard.md +35 -0
- package/commands/orchestrate.md +21 -0
- package/commands/plan-feature.md +36 -0
- package/commands/postmortem.md +42 -0
- package/commands/pr-describe.md +39 -0
- package/commands/pr.md +22 -0
- package/commands/prd.md +41 -0
- package/commands/project-init.md +21 -0
- package/commands/refactor-clean.md +24 -0
- package/commands/resume-session.md +22 -0
- package/commands/review.md +35 -0
- package/commands/save-session.md +37 -0
- package/commands/security-scan.md +32 -0
- package/commands/sessions.md +21 -0
- package/commands/skill-create.md +21 -0
- package/commands/tdd.md +36 -0
- package/commands/test-coverage.md +24 -0
- package/commands/threat-model.md +36 -0
- package/commands/update-docs.md +25 -0
- package/commands/verify.md +21 -0
- package/hooks/block-dangerous-commands/HOOK.md +30 -0
- package/hooks/block-dangerous-commands/cases.json +50 -0
- package/hooks/block-dangerous-commands/hook.js +75 -0
- package/hooks/block-no-verify/HOOK.md +25 -0
- package/hooks/block-no-verify/cases.json +30 -0
- package/hooks/block-no-verify/hook.js +43 -0
- package/hooks/format-on-edit/HOOK.md +24 -0
- package/hooks/format-on-edit/hook.js +89 -0
- package/hooks/guard-secrets/HOOK.md +19 -0
- package/hooks/guard-secrets/cases.json +110 -0
- package/hooks/guard-secrets/hook.js +44 -0
- package/hooks/load-session-context/HOOK.md +20 -0
- package/hooks/load-session-context/hook.js +56 -0
- package/hooks/protect-files/HOOK.md +24 -0
- package/hooks/protect-files/cases.json +27 -0
- package/hooks/protect-files/hook.js +39 -0
- package/hooks/run.js +132 -0
- package/mcp/atlassian.json +11 -0
- package/mcp/chrome-devtools.json +18 -0
- package/mcp/cloudflare.json +11 -0
- package/mcp/context7.json +11 -0
- package/mcp/fetch.json +17 -0
- package/mcp/figma.json +11 -0
- package/mcp/git.json +17 -0
- package/mcp/github.json +11 -0
- package/mcp/linear.json +11 -0
- package/mcp/memory.json +18 -0
- package/mcp/notion.json +11 -0
- package/mcp/playwright.json +18 -0
- package/mcp/sentry.json +11 -0
- package/mcp/stripe.json +11 -0
- package/mcp/supabase.json +11 -0
- package/mcp/time.json +17 -0
- package/package.json +59 -4
- package/profiles.json +165 -0
- package/rules/common.md +21 -0
- package/rules/cpp.md +29 -0
- package/rules/csharp.md +25 -0
- package/rules/dart.md +29 -0
- package/rules/fsharp.md +28 -0
- package/rules/git.md +26 -0
- package/rules/go.md +29 -0
- package/rules/java.md +29 -0
- package/rules/kotlin.md +27 -0
- package/rules/perl.md +26 -0
- package/rules/php.md +28 -0
- package/rules/python.md +30 -0
- package/rules/ruby.md +27 -0
- package/rules/rust.md +26 -0
- package/rules/security.md +29 -0
- package/rules/swift.md +28 -0
- package/rules/testing.md +25 -0
- package/rules/typescript.md +29 -0
- package/schemas/skill-frontmatter.schema.json +52 -0
- package/scripts/lib/audit.js +96 -0
- package/scripts/lib/frontmatter.js +65 -0
- package/scripts/lib/hooks-install.js +292 -0
- package/scripts/lib/i18n.js +51 -0
- package/scripts/lib/inventory.js +85 -0
- package/scripts/lib/lint.js +158 -0
- package/scripts/lib/mcp-install.js +284 -0
- package/scripts/lib/profiles.js +128 -0
- package/scripts/lib/render.js +86 -0
- package/scripts/lib/rules-install.js +118 -0
- package/scripts/lib/skills.js +86 -0
- package/scripts/lib/targets.js +113 -0
- package/skills/accounting-api-integration/SKILL.md +81 -0
- package/skills/accounting-api-integration/evals/triggers.json +17 -0
- package/skills/accounting-api-integration/references/quickbooks.md +48 -0
- package/skills/accounting-api-integration/references/xero.md +45 -0
- package/skills/agent-harness-design/SKILL.md +125 -0
- package/skills/agent-harness-design/evals/triggers.json +21 -0
- package/skills/ai-media-generation/SKILL.md +89 -0
- package/skills/ai-media-generation/evals/triggers.json +16 -0
- package/skills/ai-media-generation/references/job-pipeline.md +76 -0
- package/skills/angular-patterns/SKILL.md +122 -0
- package/skills/angular-patterns/evals/triggers.json +16 -0
- package/skills/api-design/SKILL.md +96 -0
- package/skills/api-design/evals/triggers.json +18 -0
- package/skills/api-design/references/conventions.md +133 -0
- package/skills/architecture-decision-records/SKILL.md +98 -0
- package/skills/architecture-decision-records/evals/triggers.json +17 -0
- package/skills/article-writing/SKILL.md +108 -0
- package/skills/article-writing/evals/triggers.json +16 -0
- package/skills/aspnet-core-patterns/SKILL.md +149 -0
- package/skills/aspnet-core-patterns/evals/triggers.json +16 -0
- package/skills/auth-patterns/SKILL.md +104 -0
- package/skills/auth-patterns/evals/triggers.json +17 -0
- package/skills/auth-patterns/references/oauth-oidc.md +64 -0
- package/skills/autonomous-loops/SKILL.md +99 -0
- package/skills/autonomous-loops/evals/triggers.json +14 -0
- package/skills/aws-patterns/SKILL.md +91 -0
- package/skills/aws-patterns/evals/triggers.json +16 -0
- package/skills/azure-patterns/SKILL.md +86 -0
- package/skills/azure-patterns/evals/triggers.json +16 -0
- package/skills/benchmark-methodology/SKILL.md +88 -0
- package/skills/benchmark-methodology/evals/triggers.json +16 -0
- package/skills/benchmark-methodology/references/language-harnesses.md +130 -0
- package/skills/bgp-diagnostics/SKILL.md +84 -0
- package/skills/bgp-diagnostics/evals/triggers.json +18 -0
- package/skills/bgp-diagnostics/references/bgp-reference.md +81 -0
- package/skills/bioinformatics-workflows/SKILL.md +108 -0
- package/skills/bioinformatics-workflows/evals/triggers.json +16 -0
- package/skills/bioinformatics-workflows/references/commands.md +71 -0
- package/skills/bioinformatics-workflows/references/formats.md +24 -0
- package/skills/bioinformatics-workflows/references/workflow-managers.md +107 -0
- package/skills/brand-voice/SKILL.md +112 -0
- package/skills/brand-voice/evals/triggers.json +16 -0
- package/skills/browser-qa/SKILL.md +99 -0
- package/skills/browser-qa/evals/triggers.json +16 -0
- package/skills/bun-runtime/SKILL.md +93 -0
- package/skills/bun-runtime/evals/triggers.json +16 -0
- package/skills/cisco-ios-config/SKILL.md +101 -0
- package/skills/cisco-ios-config/evals/triggers.json +18 -0
- package/skills/cisco-ios-config/references/verification.md +63 -0
- package/skills/clickhouse-patterns/SKILL.md +101 -0
- package/skills/clickhouse-patterns/evals/triggers.json +18 -0
- package/skills/clickhouse-patterns/references/engines-and-views.md +138 -0
- package/skills/clinical-decision-support/SKILL.md +90 -0
- package/skills/clinical-decision-support/evals/triggers.json +16 -0
- package/skills/clinical-decision-support/references/cds-hooks.md +76 -0
- package/skills/clinical-decision-support/references/regulatory-boundaries.md +37 -0
- package/skills/code-tour/SKILL.md +97 -0
- package/skills/code-tour/evals/triggers.json +14 -0
- package/skills/codebase-onboarding/SKILL.md +112 -0
- package/skills/codebase-onboarding/evals/triggers.json +14 -0
- package/skills/cold-outreach-sequences/SKILL.md +90 -0
- package/skills/cold-outreach-sequences/evals/triggers.json +16 -0
- package/skills/content-repurposing/SKILL.md +82 -0
- package/skills/content-repurposing/evals/triggers.json +16 -0
- package/skills/context-budget/SKILL.md +84 -0
- package/skills/context-budget/evals/triggers.json +14 -0
- package/skills/conventional-commits/SKILL.md +144 -0
- package/skills/conventional-commits/evals/triggers.json +16 -0
- package/skills/cpp-patterns/SKILL.md +129 -0
- package/skills/cpp-patterns/evals/triggers.json +16 -0
- package/skills/cpp-patterns/references/cmake-and-tooling.md +88 -0
- package/skills/cpp-patterns/references/undefined-behaviour.md +75 -0
- package/skills/cpp-testing/SKILL.md +131 -0
- package/skills/cpp-testing/evals/triggers.json +16 -0
- package/skills/csharp-patterns/SKILL.md +110 -0
- package/skills/csharp-patterns/evals/triggers.json +17 -0
- package/skills/csharp-patterns/references/analyzers.md +59 -0
- package/skills/csharp-testing/SKILL.md +85 -0
- package/skills/csharp-testing/evals/triggers.json +17 -0
- package/skills/csharp-testing/references/integration-tests.md +77 -0
- package/skills/css-architecture/SKILL.md +143 -0
- package/skills/css-architecture/evals/triggers.json +16 -0
- package/skills/customs-trade-compliance/SKILL.md +95 -0
- package/skills/customs-trade-compliance/evals/triggers.json +16 -0
- package/skills/customs-trade-compliance/references/incoterms-2020.md +42 -0
- package/skills/dart-patterns/SKILL.md +157 -0
- package/skills/dart-patterns/evals/triggers.json +18 -0
- package/skills/dart-testing/SKILL.md +164 -0
- package/skills/dart-testing/evals/triggers.json +18 -0
- package/skills/data-analysis-notebooks/SKILL.md +113 -0
- package/skills/data-analysis-notebooks/evals/triggers.json +16 -0
- package/skills/data-analysis-notebooks/references/dataframe-pitfalls.md +49 -0
- package/skills/data-analysis-notebooks/references/statistics-pitfalls.md +24 -0
- package/skills/data-pipelines/SKILL.md +90 -0
- package/skills/data-pipelines/evals/triggers.json +16 -0
- package/skills/data-pipelines/references/orchestrators.md +64 -0
- package/skills/database-migrations/SKILL.md +102 -0
- package/skills/database-migrations/evals/triggers.json +16 -0
- package/skills/database-migrations/references/recipes.md +98 -0
- package/skills/database-migrations/references/tools.md +21 -0
- package/skills/dbt-patterns/SKILL.md +96 -0
- package/skills/dbt-patterns/evals/triggers.json +16 -0
- package/skills/dbt-patterns/references/yaml-examples.md +114 -0
- package/skills/deep-research/SKILL.md +107 -0
- package/skills/deep-research/evals/triggers.json +14 -0
- package/skills/dependency-audit/SKILL.md +103 -0
- package/skills/dependency-audit/evals/triggers.json +16 -0
- package/skills/dependency-audit/references/tools.md +124 -0
- package/skills/deployment-strategies/SKILL.md +108 -0
- package/skills/deployment-strategies/evals/triggers.json +16 -0
- package/skills/django-patterns/SKILL.md +123 -0
- package/skills/django-patterns/evals/triggers.json +16 -0
- package/skills/docker-patterns/SKILL.md +132 -0
- package/skills/docker-patterns/evals/triggers.json +16 -0
- package/skills/docker-patterns/references/dockerfiles.md +105 -0
- package/skills/e2e-testing-playwright/SKILL.md +94 -0
- package/skills/e2e-testing-playwright/evals/triggers.json +16 -0
- package/skills/e2e-testing-playwright/references/config-and-ci.md +129 -0
- package/skills/ef-core-patterns/SKILL.md +117 -0
- package/skills/ef-core-patterns/evals/triggers.json +16 -0
- package/skills/email-deliverability/SKILL.md +99 -0
- package/skills/email-deliverability/evals/triggers.json +17 -0
- package/skills/email-deliverability/references/dns-records.md +85 -0
- package/skills/error-handling-patterns/SKILL.md +112 -0
- package/skills/error-handling-patterns/evals/triggers.json +18 -0
- package/skills/error-handling-patterns/references/language-idioms.md +49 -0
- package/skills/estimation-and-scoping/SKILL.md +97 -0
- package/skills/estimation-and-scoping/evals/triggers.json +14 -0
- package/skills/evm-dapp-integration/SKILL.md +73 -0
- package/skills/evm-dapp-integration/evals/triggers.json +16 -0
- package/skills/fastapi-patterns/SKILL.md +127 -0
- package/skills/fastapi-patterns/evals/triggers.json +16 -0
- package/skills/ffmpeg-media-processing/SKILL.md +94 -0
- package/skills/ffmpeg-media-processing/evals/triggers.json +16 -0
- package/skills/ffmpeg-media-processing/references/recipes.md +112 -0
- package/skills/fhir-integration/SKILL.md +81 -0
- package/skills/fhir-integration/evals/triggers.json +16 -0
- package/skills/fhir-integration/references/search-and-bundles.md +73 -0
- package/skills/fhir-integration/references/smart-on-fhir.md +50 -0
- package/skills/flask-patterns/SKILL.md +132 -0
- package/skills/flask-patterns/evals/triggers.json +16 -0
- package/skills/flutter-patterns/SKILL.md +99 -0
- package/skills/flutter-patterns/evals/triggers.json +16 -0
- package/skills/flutterwave-integration/SKILL.md +75 -0
- package/skills/flutterwave-integration/evals/triggers.json +16 -0
- package/skills/flutterwave-integration/references/api.md +73 -0
- package/skills/flutterwave-integration/references/webhooks.md +49 -0
- package/skills/fsharp-patterns/SKILL.md +133 -0
- package/skills/fsharp-patterns/evals/triggers.json +16 -0
- package/skills/fsharp-testing/SKILL.md +136 -0
- package/skills/fsharp-testing/evals/triggers.json +16 -0
- package/skills/gcp-patterns/SKILL.md +86 -0
- package/skills/gcp-patterns/evals/triggers.json +16 -0
- package/skills/git-workflow/SKILL.md +114 -0
- package/skills/git-workflow/evals/triggers.json +16 -0
- package/skills/github-actions-ci/SKILL.md +117 -0
- package/skills/github-actions-ci/evals/triggers.json +16 -0
- package/skills/github-api-automation/SKILL.md +101 -0
- package/skills/github-api-automation/evals/triggers.json +17 -0
- package/skills/go-patterns/SKILL.md +118 -0
- package/skills/go-patterns/evals/triggers.json +17 -0
- package/skills/go-patterns/references/concurrency.md +162 -0
- package/skills/go-testing/SKILL.md +154 -0
- package/skills/go-testing/evals/triggers.json +17 -0
- package/skills/hexagonal-architecture/SKILL.md +89 -0
- package/skills/hexagonal-architecture/evals/triggers.json +17 -0
- package/skills/hipaa-compliance/SKILL.md +91 -0
- package/skills/hipaa-compliance/evals/triggers.json +18 -0
- package/skills/hipaa-compliance/references/safeguards.md +68 -0
- package/skills/hl7v2-integration/SKILL.md +69 -0
- package/skills/hl7v2-integration/evals/triggers.json +16 -0
- package/skills/hl7v2-integration/references/messages-and-ack.md +78 -0
- package/skills/homelab-network-setup/SKILL.md +125 -0
- package/skills/homelab-network-setup/evals/triggers.json +18 -0
- package/skills/homelab-network-setup/references/firewall-and-dns.md +45 -0
- package/skills/homelab-network-setup/references/wireguard.md +72 -0
- package/skills/hubtel-integration/SKILL.md +77 -0
- package/skills/hubtel-integration/evals/triggers.json +16 -0
- package/skills/hubtel-integration/references/api.md +92 -0
- package/skills/i18n-localization/SKILL.md +114 -0
- package/skills/i18n-localization/evals/triggers.json +16 -0
- package/skills/i18n-localization/references/libraries.md +52 -0
- package/skills/incident-postmortem/SKILL.md +118 -0
- package/skills/incident-postmortem/evals/triggers.json +14 -0
- package/skills/inventory-demand-planning/SKILL.md +89 -0
- package/skills/inventory-demand-planning/evals/triggers.json +16 -0
- package/skills/inventory-demand-planning/references/formulas.md +62 -0
- package/skills/investor-materials/SKILL.md +127 -0
- package/skills/investor-materials/evals/triggers.json +16 -0
- package/skills/investor-materials/references/templates.md +74 -0
- package/skills/invoicing-and-tax/SKILL.md +84 -0
- package/skills/invoicing-and-tax/evals/triggers.json +17 -0
- package/skills/invoicing-and-tax/references/e-invoicing.md +24 -0
- package/skills/invoicing-and-tax/references/invoice-fields.md +38 -0
- package/skills/invoicing-and-tax/references/rounding.md +26 -0
- package/skills/java-patterns/SKILL.md +141 -0
- package/skills/java-patterns/evals/triggers.json +17 -0
- package/skills/java-testing/SKILL.md +142 -0
- package/skills/java-testing/evals/triggers.json +16 -0
- package/skills/jetpack-compose-patterns/SKILL.md +88 -0
- package/skills/jetpack-compose-patterns/evals/triggers.json +16 -0
- package/skills/jira-integration/SKILL.md +92 -0
- package/skills/jira-integration/evals/triggers.json +17 -0
- package/skills/jpa-hibernate/SKILL.md +92 -0
- package/skills/jpa-hibernate/evals/triggers.json +18 -0
- package/skills/jpa-hibernate/references/mapping.md +99 -0
- package/skills/kotlin-multiplatform/SKILL.md +97 -0
- package/skills/kotlin-multiplatform/evals/triggers.json +16 -0
- package/skills/kotlin-patterns/SKILL.md +128 -0
- package/skills/kotlin-patterns/evals/triggers.json +16 -0
- package/skills/kotlin-testing/SKILL.md +117 -0
- package/skills/kotlin-testing/evals/triggers.json +16 -0
- package/skills/ktor-patterns/SKILL.md +135 -0
- package/skills/ktor-patterns/evals/triggers.json +18 -0
- package/skills/kubernetes-patterns/SKILL.md +106 -0
- package/skills/kubernetes-patterns/evals/triggers.json +16 -0
- package/skills/kubernetes-patterns/references/manifests.md +115 -0
- package/skills/laravel-patterns/SKILL.md +136 -0
- package/skills/laravel-patterns/evals/triggers.json +16 -0
- package/skills/linear-integration/SKILL.md +98 -0
- package/skills/linear-integration/evals/triggers.json +17 -0
- package/skills/literature-review/SKILL.md +109 -0
- package/skills/literature-review/evals/triggers.json +16 -0
- package/skills/literature-review/references/appraisal.md +41 -0
- package/skills/literature-review/references/search-sources.md +39 -0
- package/skills/llm-app-patterns/SKILL.md +97 -0
- package/skills/llm-app-patterns/evals/triggers.json +17 -0
- package/skills/llm-evals/SKILL.md +98 -0
- package/skills/llm-evals/evals/triggers.json +16 -0
- package/skills/llm-evals/references/graders.md +66 -0
- package/skills/logistics-exception-management/SKILL.md +90 -0
- package/skills/logistics-exception-management/evals/triggers.json +16 -0
- package/skills/manim-animations/SKILL.md +106 -0
- package/skills/manim-animations/evals/triggers.json +16 -0
- package/skills/market-research/SKILL.md +117 -0
- package/skills/market-research/evals/triggers.json +16 -0
- package/skills/marketing-campaign/SKILL.md +111 -0
- package/skills/marketing-campaign/evals/triggers.json +16 -0
- package/skills/mcp-server-patterns/SKILL.md +127 -0
- package/skills/mcp-server-patterns/evals/triggers.json +18 -0
- package/skills/mcp-server-patterns/references/protocol-notes.md +68 -0
- package/skills/metrics-dashboards/SKILL.md +113 -0
- package/skills/metrics-dashboards/evals/triggers.json +16 -0
- package/skills/ml-experiment-workflow/SKILL.md +125 -0
- package/skills/ml-experiment-workflow/evals/triggers.json +16 -0
- package/skills/mongodb-patterns/SKILL.md +115 -0
- package/skills/mongodb-patterns/evals/triggers.json +18 -0
- package/skills/mongodb-patterns/references/indexes-and-aggregation.md +94 -0
- package/skills/mpesa-daraja-integration/SKILL.md +70 -0
- package/skills/mpesa-daraja-integration/evals/triggers.json +16 -0
- package/skills/mpesa-daraja-integration/references/api.md +103 -0
- package/skills/mpesa-daraja-integration/references/callbacks.md +74 -0
- package/skills/mtn-momo-integration/SKILL.md +66 -0
- package/skills/mtn-momo-integration/evals/triggers.json +16 -0
- package/skills/mtn-momo-integration/references/api.md +153 -0
- package/skills/mtn-momo-integration/references/callbacks.md +92 -0
- package/skills/mtn-momo-integration/references/sandbox-and-go-live.md +51 -0
- package/skills/mtn-momo-integration/scripts/sandbox-setup.js +79 -0
- package/skills/mysql-patterns/SKILL.md +96 -0
- package/skills/mysql-patterns/evals/triggers.json +16 -0
- package/skills/mysql-patterns/references/explain.md +34 -0
- package/skills/mysql-patterns/references/online-ddl.md +41 -0
- package/skills/nestjs-patterns/SKILL.md +114 -0
- package/skills/nestjs-patterns/evals/triggers.json +16 -0
- package/skills/nestjs-patterns/references/testing.md +68 -0
- package/skills/network-automation/SKILL.md +107 -0
- package/skills/network-automation/evals/triggers.json +18 -0
- package/skills/network-automation/references/tool-snippets.md +153 -0
- package/skills/network-troubleshooting/SKILL.md +152 -0
- package/skills/network-troubleshooting/evals/triggers.json +18 -0
- package/skills/network-troubleshooting/references/commands.md +33 -0
- package/skills/nextjs-patterns/SKILL.md +150 -0
- package/skills/nextjs-patterns/evals/triggers.json +18 -0
- package/skills/nextjs-patterns/references/caching.md +87 -0
- package/skills/node-backend-patterns/SKILL.md +103 -0
- package/skills/node-backend-patterns/evals/triggers.json +16 -0
- package/skills/node-backend-patterns/references/templates.md +122 -0
- package/skills/nuxt-patterns/SKILL.md +103 -0
- package/skills/nuxt-patterns/evals/triggers.json +16 -0
- package/skills/parallel-agent-orchestration/SKILL.md +106 -0
- package/skills/parallel-agent-orchestration/evals/triggers.json +14 -0
- package/skills/paystack-integration/SKILL.md +59 -0
- package/skills/paystack-integration/evals/triggers.json +14 -0
- package/skills/paystack-integration/references/api.md +73 -0
- package/skills/paystack-integration/references/mobile-money.md +50 -0
- package/skills/paystack-integration/references/webhooks.md +80 -0
- package/skills/pci-dss-scope/SKILL.md +103 -0
- package/skills/pci-dss-scope/evals/triggers.json +18 -0
- package/skills/pci-dss-scope/references/saq-and-data.md +36 -0
- package/skills/perl-patterns/SKILL.md +135 -0
- package/skills/perl-patterns/evals/triggers.json +18 -0
- package/skills/perl-testing/SKILL.md +126 -0
- package/skills/perl-testing/evals/triggers.json +17 -0
- package/skills/php-patterns/SKILL.md +116 -0
- package/skills/php-patterns/evals/triggers.json +16 -0
- package/skills/php-testing/SKILL.md +123 -0
- package/skills/php-testing/evals/triggers.json +16 -0
- package/skills/postgres-patterns/SKILL.md +103 -0
- package/skills/postgres-patterns/evals/triggers.json +17 -0
- package/skills/postgres-patterns/references/explain.md +59 -0
- package/skills/postgres-patterns/references/locks.md +53 -0
- package/skills/prd-writing/SKILL.md +124 -0
- package/skills/prd-writing/evals/triggers.json +15 -0
- package/skills/prisma-patterns/SKILL.md +83 -0
- package/skills/prisma-patterns/evals/triggers.json +18 -0
- package/skills/prisma-patterns/references/prisma-8.md +46 -0
- package/skills/prisma-patterns/references/workflow.md +50 -0
- package/skills/privacy-compliance/SKILL.md +121 -0
- package/skills/privacy-compliance/evals/triggers.json +18 -0
- package/skills/privacy-compliance/references/jurisdictions.md +51 -0
- package/skills/production-scheduling/SKILL.md +98 -0
- package/skills/production-scheduling/evals/triggers.json +16 -0
- package/skills/prompt-engineering/SKILL.md +92 -0
- package/skills/prompt-engineering/evals/triggers.json +16 -0
- package/skills/python-patterns/SKILL.md +107 -0
- package/skills/python-patterns/evals/triggers.json +16 -0
- package/skills/python-patterns/references/tooling.md +101 -0
- package/skills/python-testing/SKILL.md +126 -0
- package/skills/python-testing/evals/triggers.json +16 -0
- package/skills/python-testing/references/mocking.md +71 -0
- package/skills/pytorch-patterns/SKILL.md +139 -0
- package/skills/pytorch-patterns/evals/triggers.json +16 -0
- package/skills/pytorch-patterns/references/debugging.md +39 -0
- package/skills/quarkus-patterns/SKILL.md +93 -0
- package/skills/quarkus-patterns/evals/triggers.json +18 -0
- package/skills/rag-pipeline-patterns/SKILL.md +117 -0
- package/skills/rag-pipeline-patterns/evals/triggers.json +18 -0
- package/skills/rag-pipeline-patterns/references/evaluation.md +37 -0
- package/skills/rag-pipeline-patterns/references/retrieval-recipes.md +78 -0
- package/skills/rails-patterns/SKILL.md +132 -0
- package/skills/rails-patterns/evals/triggers.json +16 -0
- package/skills/react-native-patterns/SKILL.md +101 -0
- package/skills/react-native-patterns/evals/triggers.json +16 -0
- package/skills/react-patterns/SKILL.md +140 -0
- package/skills/react-patterns/evals/triggers.json +18 -0
- package/skills/react-performance/SKILL.md +107 -0
- package/skills/react-performance/evals/triggers.json +18 -0
- package/skills/redis-patterns/SKILL.md +109 -0
- package/skills/redis-patterns/evals/triggers.json +18 -0
- package/skills/redis-patterns/references/operations.md +61 -0
- package/skills/refactoring-safely/SKILL.md +107 -0
- package/skills/refactoring-safely/evals/triggers.json +17 -0
- package/skills/release-management/SKILL.md +141 -0
- package/skills/release-management/evals/triggers.json +16 -0
- package/skills/remotion-video/SKILL.md +91 -0
- package/skills/remotion-video/evals/triggers.json +16 -0
- package/skills/remotion-video/references/lambda.md +66 -0
- package/skills/returns-reverse-logistics/SKILL.md +97 -0
- package/skills/returns-reverse-logistics/evals/triggers.json +16 -0
- package/skills/ruby-patterns/SKILL.md +127 -0
- package/skills/ruby-patterns/evals/triggers.json +18 -0
- package/skills/ruby-testing/SKILL.md +113 -0
- package/skills/ruby-testing/evals/triggers.json +18 -0
- package/skills/runbook-writing/SKILL.md +101 -0
- package/skills/runbook-writing/evals/triggers.json +14 -0
- package/skills/rust-patterns/SKILL.md +118 -0
- package/skills/rust-patterns/evals/triggers.json +16 -0
- package/skills/rust-patterns/references/cargo-and-clippy.md +105 -0
- package/skills/rust-patterns/references/errors-and-traits.md +97 -0
- package/skills/rust-testing/SKILL.md +148 -0
- package/skills/rust-testing/evals/triggers.json +16 -0
- package/skills/rust-testing/references/fakes-proptest-async.md +113 -0
- package/skills/search-first/SKILL.md +73 -0
- package/skills/search-first/evals/triggers.json +14 -0
- package/skills/security-review/SKILL.md +58 -0
- package/skills/security-review/evals/triggers.json +14 -0
- package/skills/security-review/references/checklist.md +54 -0
- package/skills/security-review/scripts/scan-secrets.js +79 -0
- package/skills/seo-audit/SKILL.md +111 -0
- package/skills/seo-audit/evals/triggers.json +16 -0
- package/skills/seo-audit/references/checks.md +65 -0
- package/skills/session-handoff/SKILL.md +101 -0
- package/skills/session-handoff/evals/triggers.json +14 -0
- package/skills/skill-authoring/SKILL.md +87 -0
- package/skills/skill-authoring/evals/triggers.json +14 -0
- package/skills/skill-authoring/references/frontmatter.md +40 -0
- package/skills/slack-app-integration/SKILL.md +108 -0
- package/skills/slack-app-integration/evals/triggers.json +17 -0
- package/skills/smart-contract-security/SKILL.md +110 -0
- package/skills/smart-contract-security/evals/triggers.json +16 -0
- package/skills/smart-contract-security/references/vulnerability-checklist.md +79 -0
- package/skills/social-media-publishing/SKILL.md +84 -0
- package/skills/social-media-publishing/evals/triggers.json +17 -0
- package/skills/social-media-publishing/references/platform-apis.md +116 -0
- package/skills/solidity-patterns/SKILL.md +95 -0
- package/skills/solidity-patterns/evals/triggers.json +16 -0
- package/skills/spring-boot-patterns/SKILL.md +128 -0
- package/skills/spring-boot-patterns/evals/triggers.json +18 -0
- package/skills/spring-boot-patterns/references/testing.md +72 -0
- package/skills/stripe-integration/SKILL.md +69 -0
- package/skills/stripe-integration/evals/triggers.json +16 -0
- package/skills/stripe-integration/references/api.md +71 -0
- package/skills/stripe-integration/references/billing.md +58 -0
- package/skills/stripe-integration/references/webhooks.md +76 -0
- package/skills/subscription-billing-ops/SKILL.md +118 -0
- package/skills/subscription-billing-ops/evals/triggers.json +17 -0
- package/skills/subscription-billing-ops/references/proration.md +47 -0
- package/skills/subscription-billing-ops/references/revenue-metrics.md +38 -0
- package/skills/svelte-patterns/SKILL.md +122 -0
- package/skills/svelte-patterns/evals/triggers.json +16 -0
- package/skills/swift-patterns/SKILL.md +113 -0
- package/skills/swift-patterns/evals/triggers.json +16 -0
- package/skills/swift-patterns/references/concurrency-migration.md +40 -0
- package/skills/swift-testing/SKILL.md +121 -0
- package/skills/swift-testing/evals/triggers.json +16 -0
- package/skills/swift-testing/references/swift-testing-cheatsheet.md +81 -0
- package/skills/swiftui-patterns/SKILL.md +102 -0
- package/skills/swiftui-patterns/evals/triggers.json +16 -0
- package/skills/tdd-workflow/SKILL.md +67 -0
- package/skills/tdd-workflow/evals/triggers.json +14 -0
- package/skills/technical-writing/SKILL.md +95 -0
- package/skills/technical-writing/evals/triggers.json +14 -0
- package/skills/terraform-patterns/SKILL.md +122 -0
- package/skills/terraform-patterns/evals/triggers.json +16 -0
- package/skills/terraform-patterns/references/backends.md +67 -0
- package/skills/test-coverage-improvement/SKILL.md +107 -0
- package/skills/test-coverage-improvement/evals/triggers.json +16 -0
- package/skills/threat-modeling/SKILL.md +113 -0
- package/skills/threat-modeling/evals/triggers.json +16 -0
- package/skills/typescript-patterns/SKILL.md +122 -0
- package/skills/typescript-patterns/evals/triggers.json +16 -0
- package/skills/typescript-testing/SKILL.md +101 -0
- package/skills/typescript-testing/evals/triggers.json +16 -0
- package/skills/verification-loop/SKILL.md +70 -0
- package/skills/verification-loop/evals/triggers.json +14 -0
- package/skills/verification-loop/scripts/detect-checks.js +102 -0
- package/skills/vite-patterns/SKILL.md +117 -0
- package/skills/vite-patterns/evals/triggers.json +16 -0
- package/skills/vue-patterns/SKILL.md +104 -0
- package/skills/vue-patterns/evals/triggers.json +16 -0
- package/skills/web-accessibility/SKILL.md +119 -0
- package/skills/web-accessibility/evals/triggers.json +16 -0
- package/skills/web-performance/SKILL.md +107 -0
- package/skills/web-performance/evals/triggers.json +16 -0
- package/skills/web-scraping/SKILL.md +80 -0
- package/skills/web-scraping/evals/triggers.json +16 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-patterns
|
|
3
|
+
description: React component design rules for function components and hooks, covering where state should live, the Rules of Hooks, effects used only to synchronise with external systems, data fetching without race conditions, controlled and uncontrolled forms with React 19 actions, error boundaries, choosing context versus a state library, and accessible markup. Use when building or restructuring React components, deciding where a piece of state belongs, writing or removing a useEffect, wiring up a form, or picking between context, Zustand, Redux or a query cache.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Applies to React 18 and 19 with any bundler or framework (Vite, Next.js, Remix, React Router). Needs Node.js and the project's package manager to run lint and tests.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: coding
|
|
9
|
+
kete-loom/risk: writes-files
|
|
10
|
+
kete-loom/requires: "node"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# React patterns
|
|
15
|
+
|
|
16
|
+
Write components that are pure during render, keep state as small and as local as possible, and treat effects as an escape hatch. For Next.js server/client concerns use the `nextjs-patterns` skill; for render speed use `react-performance`.
|
|
17
|
+
|
|
18
|
+
## 0. Before you change anything
|
|
19
|
+
|
|
20
|
+
1. Check the React version in `package.json` (`react`, `react-dom`). React 19 APIs (`useActionState`, `useOptimistic`, `use`, form `action` props, `ref` as a prop) don't exist in 18.
|
|
21
|
+
2. Check what the project already uses for server state (TanStack Query, SWR, RTK Query, Apollo, framework loaders) and client state (context, Zustand, Redux, Jotai). Follow it. Don't add a second library for the same job.
|
|
22
|
+
3. Check lint config for `eslint-plugin-react-hooks` (`rules-of-hooks`, `exhaustive-deps`). If it's missing, tell the user; don't silently add it.
|
|
23
|
+
|
|
24
|
+
## 1. Where state lives
|
|
25
|
+
|
|
26
|
+
Decide in this order and stop at the first that fits:
|
|
27
|
+
|
|
28
|
+
1. **Derive it.** If a value can be computed from props or other state, compute it during render. Never mirror props into state (`useState(props.x)` plus an effect to sync it).
|
|
29
|
+
2. **Local `useState`/`useReducer`** in the lowest component that needs it. Use `useReducer` when several fields change together or the next state depends on an action type.
|
|
30
|
+
3. **Lift** to the closest common parent when two siblings need it.
|
|
31
|
+
4. **URL** (search params, path) for anything that should survive reload or be shareable: filters, tabs, pagination, selected item.
|
|
32
|
+
5. **Server-state cache** (the project's query library or framework loader) for data owned by the backend. Never copy fetched data into a global store by hand.
|
|
33
|
+
6. **Context** for values many distant components read and that change rarely: theme, locale, current user, a feature-flag client.
|
|
34
|
+
7. **A state library** only when client state is large, updated from many places, and context would re-render too much.
|
|
35
|
+
|
|
36
|
+
Rules:
|
|
37
|
+
- Store the minimum: an `id`, not a copy of the selected object.
|
|
38
|
+
- Reset state with a `key` (`<Profile key={userId} />`), not with an effect.
|
|
39
|
+
- Put the state setter and value in separate contexts if consumers only need one, or memoise the context value object.
|
|
40
|
+
|
|
41
|
+
## 2. Rules of Hooks
|
|
42
|
+
|
|
43
|
+
- Call hooks only at the top level of a component or a custom hook. No hooks inside conditions, loops, nested functions, `try` blocks or after an early `return`.
|
|
44
|
+
- Custom hooks start with `use` and may call other hooks. A function that calls no hooks is not a hook; don't prefix it.
|
|
45
|
+
- Never call a component as a function (`Row()`); render it (`<Row />`).
|
|
46
|
+
- Never define a component inside another component's body. It remounts every render and loses state.
|
|
47
|
+
- Render must be pure: no mutation of props, state or module variables, no `Math.random()`/`Date.now()` driving output without a stable source, no reading or writing `ref.current` during render (except lazy init).
|
|
48
|
+
|
|
49
|
+
## 3. Effects: synchronisation only
|
|
50
|
+
|
|
51
|
+
An effect is for keeping React in sync with something outside React: a subscription, a browser API, a non-React widget, a websocket. Before writing `useEffect`, check whether you need it:
|
|
52
|
+
|
|
53
|
+
| You want to... | Do this instead |
|
|
54
|
+
|---|---|
|
|
55
|
+
| Compute a value from props/state | Compute during render (memoise only if measured slow) |
|
|
56
|
+
| Respond to a click or submit | Put the logic in the event handler |
|
|
57
|
+
| Reset state when a prop changes | Change the `key` |
|
|
58
|
+
| Adjust part of state when a prop changes | Derive it, or store the previous prop and compare during render |
|
|
59
|
+
| Notify a parent of a change | Call the parent's callback in the same handler that changed state |
|
|
60
|
+
| Fetch data | Use the project's data library or framework loader (section 4) |
|
|
61
|
+
|
|
62
|
+
When you do write one:
|
|
63
|
+
1. List every reactive value it reads in the dependency array. Never suppress `exhaustive-deps`; restructure instead (move the function inside the effect, use an updater `setX(x => x + 1)`, or move non-reactive logic to `useEffectEvent` on React 19.2+).
|
|
64
|
+
2. Return a cleanup that undoes the setup: unsubscribe, `clearInterval`, `controller.abort()`, `disconnect()`.
|
|
65
|
+
3. Expect it to run twice in development under `<StrictMode>`. If that breaks something, the cleanup is wrong; don't remove StrictMode.
|
|
66
|
+
4. Don't call `setState` synchronously in the effect body just to derive state; that is section 1, rule 1.
|
|
67
|
+
|
|
68
|
+
## 4. Data fetching
|
|
69
|
+
|
|
70
|
+
Prefer, in order: the framework's loader or a Server Component (Next.js, Remix, React Router), then the project's query library, then a hand-rolled effect.
|
|
71
|
+
|
|
72
|
+
If you must fetch in an effect, guard against races and unmounts:
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
const controller = new AbortController();
|
|
77
|
+
setStatus("loading");
|
|
78
|
+
fetch(`/api/users/${id}`, { signal: controller.signal })
|
|
79
|
+
.then((r) => { if (!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); })
|
|
80
|
+
.then((data) => { setUser(data); setStatus("done"); })
|
|
81
|
+
.catch((e) => { if (e.name !== "AbortError") { setError(e); setStatus("error"); } });
|
|
82
|
+
return () => controller.abort();
|
|
83
|
+
}, [id]);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- Model loading, error and empty states explicitly; never render `data.items.map` before data exists.
|
|
87
|
+
- Start independent requests in parallel (`Promise.all`, or sibling components each with their own query), not one after another.
|
|
88
|
+
- With `use(promise)` on React 19, create the promise outside render (in a loader, a Server Component or a cache) and wrap the reader in `<Suspense>`. A promise created during render is recreated every render.
|
|
89
|
+
|
|
90
|
+
## 5. Forms
|
|
91
|
+
|
|
92
|
+
- **Uncontrolled + `FormData`** is the default for submit-only forms: `<form action={fn}>` on React 19, or `onSubmit` with `new FormData(e.currentTarget)` on 18.
|
|
93
|
+
- **Controlled inputs** (`value` + `onChange`) only when you need the value on every keystroke: live validation, formatting, dependent fields.
|
|
94
|
+
- Never switch an input between controlled and uncontrolled; initialise with `""`, not `undefined`.
|
|
95
|
+
- React 19: `useActionState(action, initialState)` returns `[state, formAction, isPending]` for results and errors; `useFormStatus()` (from `react-dom`) reads pending state inside a child of the `<form>`; `useOptimistic` shows the expected result while the action runs.
|
|
96
|
+
- For large forms the project may use React Hook Form or similar; follow it.
|
|
97
|
+
- Validate on the server too. Client validation is UX, not security.
|
|
98
|
+
- Every input has a `<label htmlFor>` (or wraps the input); errors are linked with `aria-describedby` and announced (`role="alert"` or `aria-live="polite"`).
|
|
99
|
+
- Disable the submit button while pending, and keep it focusable.
|
|
100
|
+
|
|
101
|
+
## 6. Error boundaries
|
|
102
|
+
|
|
103
|
+
- Error boundaries catch errors thrown during render, in lifecycle methods and in constructors below them. They do **not** catch errors in event handlers, async code (timers, promises after an `await`), or the boundary itself.
|
|
104
|
+
- There is no hook equivalent: write a class with `static getDerivedStateFromError` and `componentDidCatch`, or use the project's `react-error-boundary` if it's installed. Frameworks may provide their own (`error.tsx` in Next.js).
|
|
105
|
+
- Place boundaries around independent regions (a widget, a route segment), not just once at the root. Give each a fallback with a retry that resets it (`resetKeys` or a changed `key`).
|
|
106
|
+
- Handle event-handler and async errors with `try/catch` and state; to route them into a boundary, set state and rethrow during render.
|
|
107
|
+
- Report the error in `componentDidCatch` (or the root's `onCaughtError`/`onUncaughtError` on React 19) to the project's logger.
|
|
108
|
+
|
|
109
|
+
## 7. Context vs state libraries
|
|
110
|
+
|
|
111
|
+
- Context is a dependency-injection mechanism, not a store: every consumer re-renders when the value identity changes.
|
|
112
|
+
- Split contexts by update frequency. Never put fast-changing values (mouse position, form text) in a widely consumed context.
|
|
113
|
+
- Server data belongs in a query cache, not context or Redux.
|
|
114
|
+
- Pick a store (Zustand, Redux Toolkit, Jotai) only for shared, frequently updated client state, and use selectors so components subscribe to slices.
|
|
115
|
+
|
|
116
|
+
## 8. Accessibility basics
|
|
117
|
+
|
|
118
|
+
- Use the right element: `<button>` for actions, `<a href>` for navigation, `<label>` for inputs, headings in order. A `<div onClick>` is not a button.
|
|
119
|
+
- Every image has `alt` (`alt=""` if decorative). Icon-only buttons have `aria-label`.
|
|
120
|
+
- Keyboard: everything clickable is reachable with Tab and works with Enter/Space; focus is visible; dialogs trap focus and return it on close (prefer `<dialog>` or the project's headless UI library).
|
|
121
|
+
- After route changes or async updates, move focus or announce the change.
|
|
122
|
+
- Lists use stable, unique `key`s from the data (an id), never the array index when items can be reordered, inserted or removed.
|
|
123
|
+
- For full audits use the `web-accessibility` skill.
|
|
124
|
+
|
|
125
|
+
## Never
|
|
126
|
+
|
|
127
|
+
- Never mutate state or props in place (`arr.push`, `obj.x = 1`); create new values.
|
|
128
|
+
- Never suppress `react-hooks` lint rules to make a warning go away.
|
|
129
|
+
- Never put secrets or tokens in client code or `localStorage` because "it's only the frontend".
|
|
130
|
+
- Never use `dangerouslySetInnerHTML` with unsanitised input.
|
|
131
|
+
|
|
132
|
+
## Stop and ask the user
|
|
133
|
+
|
|
134
|
+
- Before adding a new state, data or form library.
|
|
135
|
+
- Before converting many components between controlled and uncontrolled, or between class and function components, beyond the task.
|
|
136
|
+
- When the expected behaviour on error, empty or loading states isn't specified.
|
|
137
|
+
|
|
138
|
+
## Report back
|
|
139
|
+
|
|
140
|
+
End with: components changed, where each new piece of state lives and why, effects added or removed, the lint and test commands you ran with results, and anything not checked (for example "did not test with a screen reader").
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Should this filter state live in the parent component, the URL, or a context?",
|
|
4
|
+
"I have a useEffect that copies props into state and it keeps getting out of sync, how should I restructure it?",
|
|
5
|
+
"Build a signup form component in React with validation errors shown under each field",
|
|
6
|
+
"Our dashboard crashes completely when one widget throws, add error boundaries properly",
|
|
7
|
+
"Is it worth adding Zustand or should I just use React context for the cart state?",
|
|
8
|
+
"Write a custom hook that subscribes to window resize and cleans up correctly"
|
|
9
|
+
],
|
|
10
|
+
"should_not_trigger": [
|
|
11
|
+
"The product list re-renders 200 times when I type in the search box, profile it and speed it up",
|
|
12
|
+
"Should this Next.js page be a server component or a client component?",
|
|
13
|
+
"Review this React pull request for stale closures and hook violations",
|
|
14
|
+
"Set up revalidateTag caching for the blog posts in my App Router app",
|
|
15
|
+
"Write Playwright end-to-end tests for the checkout flow",
|
|
16
|
+
"Make my React Native FlatList scroll smoothly on Android"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-performance
|
|
3
|
+
description: Measure-first workflow for slow React UIs - profile renders with React DevTools and the Profiler API, cut wasted re-renders with memo, useMemo and useCallback only where the profile shows a win (or let React Compiler do it), virtualise long lists, split code with lazy and Suspense, remove request waterfalls, and shrink the JavaScript bundle. Use when a React or Next.js screen feels laggy, typing or scrolling stutters, a component re-renders too often, Interaction to Next Paint is poor, or the client bundle has grown too big.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Applies to React 18 and 19 in any framework. Needs Node.js, the project's package manager and a browser with React DevTools for profiling.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: coding
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/requires: "node"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# React performance
|
|
15
|
+
|
|
16
|
+
No optimisation without a measurement before and after. Most React slowness comes from a few causes: rendering too much, rendering too often, loading too much JavaScript, or waiting on requests one after another. Component design rules live in `react-patterns`; Next.js caching and server/client splits in `nextjs-patterns`; Core Web Vitals and non-React page speed in `web-performance`.
|
|
17
|
+
|
|
18
|
+
## 1. Define the problem
|
|
19
|
+
|
|
20
|
+
1. Get a concrete symptom from the user: which screen, which interaction (typing, scrolling, opening a panel, first load), on what device. If it's vague, ask.
|
|
21
|
+
2. Classify it:
|
|
22
|
+
- **Interaction is slow** (input lag, janky scroll): render cost. Go to 2.
|
|
23
|
+
- **First load is slow** (blank screen, late content): bundle or waterfalls. Go to 5 and 6.
|
|
24
|
+
3. Measure in a production build (`npm run build` then the project's preview/start command). Development mode and StrictMode double-render and are several times slower; never judge speed there.
|
|
25
|
+
|
|
26
|
+
## 2. Measure renders
|
|
27
|
+
|
|
28
|
+
1. React DevTools → Profiler: record the slow interaction, then read the flame graph and ranked chart. Note the slowest commits, which components rendered, and "Why did this render?" (enable "Record why each component rendered while profiling" in settings).
|
|
29
|
+
2. Turn on "Highlight updates when components render" to see what re-renders while you interact.
|
|
30
|
+
3. Use the browser Performance panel with CPU throttling (4x or 6x) for long tasks over 50 ms and layout/paint cost that DevTools won't show.
|
|
31
|
+
4. For numbers you can compare in code or CI, wrap the subtree in `<Profiler id="Grid" onRender={(id, phase, actualDuration, baseDuration) => ...}>`. `actualDuration` is time spent rendering this commit; `baseDuration` estimates a full re-render without memoisation. Profiling a production build needs the profiling build of `react-dom` (see the React docs for your bundler); `<Profiler>` adds overhead, so remove it or gate it after measuring.
|
|
32
|
+
5. Write down the baseline: commit duration and count for the interaction, or long-task total. You'll compare against it.
|
|
33
|
+
|
|
34
|
+
## 3. Fix rendering cost, cheapest first
|
|
35
|
+
|
|
36
|
+
Work down this list and re-measure after each change:
|
|
37
|
+
|
|
38
|
+
1. **Move state down.** If typing in one input re-renders the whole page, the state is too high. Move it into the component that uses it.
|
|
39
|
+
2. **Lift content up (children as props).** A parent with fast-changing state that renders `{children}` doesn't re-render those children.
|
|
40
|
+
3. **Split contexts.** One context with a frequently changing value re-renders every consumer. Split by update rate, or move fast state to a store with selectors.
|
|
41
|
+
4. **Stop recreating expensive work.** `useMemo` for a computation the profile shows is slow (rule of thumb: over ~1 ms per render) or for an object/array passed to a memoised child or an effect dependency.
|
|
42
|
+
5. **Skip unchanged subtrees.** `React.memo(Component)` on a component that renders often with the same props and is costly to render. It only helps if every prop is stable: wrap callbacks in `useCallback`, objects in `useMemo`, or pass primitives.
|
|
43
|
+
6. **Defer non-urgent updates.** `useDeferredValue(query)` for a slow results list behind a fast input; `startTransition(() => setFilter(x))` for updates that can be interrupted.
|
|
44
|
+
7. **Virtualise long lists** (section 4).
|
|
45
|
+
|
|
46
|
+
Memoisation rules:
|
|
47
|
+
- Don't wrap everything. `memo`/`useMemo`/`useCallback` cost memory and comparisons; unprofiled memoisation is noise.
|
|
48
|
+
- A memoised child receiving `onClick={() => ...}` or `style={{...}}` inline never skips; fix the prop, not the child.
|
|
49
|
+
- Keys: stable ids, never `Math.random()` or array index for lists that change. A changing key remounts the subtree, which is far slower than a re-render.
|
|
50
|
+
- If the project has React Compiler enabled (`babel-plugin-react-compiler`, or `reactCompiler: true` in Next.js 16, `experimental.reactCompiler` in 15), it memoises automatically. Don't add manual `useMemo`/`useCallback` to compiled code unless a profile shows it's still needed; do keep components pure so the compiler doesn't skip them (the `react-hooks` lint compiler rules report why).
|
|
51
|
+
- Don't enable the compiler as a side effect of a perf fix; ask first.
|
|
52
|
+
|
|
53
|
+
## 4. Long lists and big tables
|
|
54
|
+
|
|
55
|
+
- Over a few hundred rows, or rows that are expensive, render only what's visible. Use the library already in the project; otherwise `@tanstack/react-virtual` or `react-window`.
|
|
56
|
+
- Give rows a fixed or measured height; variable heights need the library's measurement API.
|
|
57
|
+
- Keep row components memoised with stable props, and keep scroll position, focus and keyboard navigation working. Check that Ctrl+F and screen readers are acceptable for the use case, or paginate instead.
|
|
58
|
+
- For simple long pages, CSS `content-visibility: auto` with `contain-intrinsic-size` can be enough without a library.
|
|
59
|
+
|
|
60
|
+
## 5. Waterfalls
|
|
61
|
+
|
|
62
|
+
A waterfall is request B starting only after request A finishes when it didn't need A.
|
|
63
|
+
|
|
64
|
+
1. Open the Network panel (throttled to "Fast 4G") and look for staircase-shaped requests.
|
|
65
|
+
2. Common causes and fixes:
|
|
66
|
+
- Parent fetches, renders child, child fetches: hoist both fetches to the route loader or Server Component and start them together (`Promise.all`), or prefetch.
|
|
67
|
+
- Sequential `await`s in one function for independent data: start promises first, await later.
|
|
68
|
+
- `React.lazy` component that then fetches its data: start the data request in parallel with loading the code (route loader, prefetch on hover/intent).
|
|
69
|
+
- A `<Suspense>` boundary around each sibling that each suspends in turn: put siblings that load together under one boundary, separate ones under their own.
|
|
70
|
+
3. In Next.js, fetch on the server and stream with `<Suspense>`; see `nextjs-patterns`.
|
|
71
|
+
|
|
72
|
+
## 6. Bundle size and code splitting
|
|
73
|
+
|
|
74
|
+
1. Measure what ships:
|
|
75
|
+
- Vite/Rollup: `rollup-plugin-visualizer`.
|
|
76
|
+
- webpack: `webpack-bundle-analyzer`.
|
|
77
|
+
- Next.js with Turbopack (16.1+): `npx next analyze` (`--output` writes to `.next/diagnostics/analyze` for before/after diffs). Next.js on webpack: `@next/bundle-analyzer` wrapped in `next.config` with `enabled: process.env.ANALYZE === "true"`, then `ANALYZE=true npm run build`.
|
|
78
|
+
- Any build with source maps: `source-map-explorer`.
|
|
79
|
+
2. Look for: a large dependency used for one function (date, lodash, icon or chart libraries), duplicates of the same package at two versions, server-only or test code in the client bundle, and polyfills you don't need.
|
|
80
|
+
3. Fix, in order: import only what you use (`import debounce from "lodash/debounce"`, per-icon imports); replace heavy libraries with platform APIs (`Intl.DateTimeFormat`, `structuredClone`) or lighter ones (ask before swapping); dedupe versions.
|
|
81
|
+
4. Split by route first, then by heavy, rarely used components (editors, charts, maps, modals): `const Chart = lazy(() => import("./Chart"))` inside `<Suspense fallback>`; in Next.js use `next/dynamic` or keep the component on the server.
|
|
82
|
+
5. Don't split tiny components; each chunk costs a request.
|
|
83
|
+
6. Re-run the analyser and record the size change (gzip/brotli).
|
|
84
|
+
|
|
85
|
+
## Never
|
|
86
|
+
|
|
87
|
+
- Never claim a speed-up without before/after numbers from the same build type and device profile.
|
|
88
|
+
- Never measure in development mode.
|
|
89
|
+
- Never suppress `exhaustive-deps` to "stop an effect re-running"; fix the unstable dependency.
|
|
90
|
+
- Never add a virtualisation, state or memo library when moving state or fixing a prop solves it.
|
|
91
|
+
|
|
92
|
+
## Stop and ask the user
|
|
93
|
+
|
|
94
|
+
- When the symptom or target (for example "INP under 200 ms on a mid-range Android") isn't stated.
|
|
95
|
+
- Before replacing a dependency, enabling React Compiler, or changing list UX (virtualising, paginating).
|
|
96
|
+
- When the fix needs a design change (removing a feature from a screen, changing data shape on the API).
|
|
97
|
+
|
|
98
|
+
## Report back
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
Symptom: <screen + interaction>
|
|
102
|
+
Baseline: <metric, build type, device/throttle>
|
|
103
|
+
Cause: <what the profile showed>
|
|
104
|
+
Change: <files and what changed>
|
|
105
|
+
After: <same metric, same conditions>
|
|
106
|
+
Not checked: <e.g. low-end mobile, Safari>
|
|
107
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Typing in the search box lags badly because the whole results table re-renders on every keystroke",
|
|
4
|
+
"Use the React Profiler to find out why my dashboard is slow when I toggle the sidebar",
|
|
5
|
+
"Our main JS chunk is 1.8 MB, help me code split the React app and find what's bloating it",
|
|
6
|
+
"Rendering 10,000 rows in this React table freezes the browser, should I virtualise it?",
|
|
7
|
+
"Is wrapping every component in React.memo and useCallback actually helping or hurting?",
|
|
8
|
+
"The page loads data in a staircase: user, then orders, then items. Fix the waterfall in our React app"
|
|
9
|
+
],
|
|
10
|
+
"should_not_trigger": [
|
|
11
|
+
"Should the shopping cart state live in context or in Zustand?",
|
|
12
|
+
"How do I write a server action that updates a profile in Next.js App Router?",
|
|
13
|
+
"Benchmark these two sorting functions in Node and tell me which is faster",
|
|
14
|
+
"Improve the Lighthouse SEO score and meta tags on our marketing site",
|
|
15
|
+
"Review this React PR for hook rule violations and missing keys",
|
|
16
|
+
"Our Postgres queries are slow, add the right indexes"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: redis-patterns
|
|
3
|
+
description: Redis and Valkey design for application code, covering which data structure fits each job, key naming and TTLs, cache-aside with stampede protection, single-instance locks and their limits, Streams consumer groups versus pub/sub, and maxmemory, eviction and persistence settings. Use when adding a Redis cache, rate limiter, queue, leaderboard or lock, reviewing Redis keys and commands for memory or latency problems, or choosing eviction and AOF or RDB settings.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Commands are Redis protocol and work with redis-cli; examples apply to any client library. Newer commands (hash field TTLs, DELEX) need a recent server, so check the version first.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: data
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/requires: "redis-cli"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Redis patterns
|
|
15
|
+
|
|
16
|
+
Use Redis for what it is good at: small, hot, mostly derived data with known access paths. Every key has an owner, a shape and a lifetime. If a design breaks one of the rules below, say so before writing code.
|
|
17
|
+
|
|
18
|
+
## 0. Before you start
|
|
19
|
+
|
|
20
|
+
1. Find the client library and how it connects (single node, Sentinel, Cluster, or a managed service). Search the codebase for the client constructor and the connection URL variable.
|
|
21
|
+
2. Find the server version: `redis-cli INFO server` (look at `redis_version`, or `valkey_version` on Valkey). Only use a command if that version has it. The command pages on redis.io list a "since" version.
|
|
22
|
+
3. Decide whether this Redis is a **cache** (losing data is fine) or a **store** (losing data is an incident). **Stop and ask the user** if you can't tell; the eviction and persistence settings depend on it.
|
|
23
|
+
4. Never run `KEYS *`, `FLUSHALL`, `FLUSHDB`, `DEBUG` or `CONFIG SET` against a shared or production instance. Use `SCAN` with `MATCH` and `COUNT` to inspect keys, and ask before any write to a non-local server.
|
|
24
|
+
|
|
25
|
+
## 1. Pick the data structure by access pattern
|
|
26
|
+
|
|
27
|
+
| Need | Use | Not |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| One cached value or counter | String (`SET`/`GET`, `INCR`) | A JSON blob you rewrite to change one field |
|
|
30
|
+
| An object whose fields you read or update separately | Hash (`HSET`, `HGET`, `HINCRBY`) | One key per field |
|
|
31
|
+
| Ranking, time-ordered index, sliding-window limiter | Sorted set (`ZADD`, `ZRANGE ... BYSCORE`, `ZREMRANGEBYSCORE`) | A list you sort in the app |
|
|
32
|
+
| Membership or deduplication | Set (`SADD`, `SISMEMBER`) | A list scanned with `LRANGE` |
|
|
33
|
+
| Approximate unique counts | HyperLogLog (`PFADD`, `PFCOUNT`) | A set holding millions of IDs |
|
|
34
|
+
| A durable work queue with acknowledgement | Stream with a consumer group | A list with `BLPOP` when losing a job matters |
|
|
35
|
+
| Fire-and-forget fan-out to connected clients | Pub/sub | Pub/sub for anything that must not be lost |
|
|
36
|
+
|
|
37
|
+
Keep each value small, ideally under about 100 KB. Big values and big collections block the single command thread while they are read, written or deleted. Delete big keys with `UNLINK`, not `DEL`, and page through big collections with `HSCAN`, `SSCAN` or `ZSCAN`.
|
|
38
|
+
|
|
39
|
+
## 2. Key design and TTLs
|
|
40
|
+
|
|
41
|
+
- Name keys `<app>:<entity>:<id>[:<facet>]`, for example `shop:user:42:cart`. Lower case, colon-separated, no user-controlled text that isn't validated or hashed.
|
|
42
|
+
- Version cache keys when the cached shape changes (`shop:v2:product:9`), so old and new code don't read each other's values during a deploy.
|
|
43
|
+
- **Every cache key gets a TTL when it is written**: `SET key value EX 300`, not `SET` followed by a separate `EXPIRE` that can be skipped if the process dies. A plain `SET` without `KEEPTTL` removes an existing TTL.
|
|
44
|
+
- Add jitter to TTLs (for example 300 seconds plus or minus 10 percent) so keys written together don't expire together.
|
|
45
|
+
- Hash fields can carry their own TTL with `HEXPIRE` on Redis 7.4 and later. On older servers, expire the whole hash or use separate keys.
|
|
46
|
+
- In Redis Cluster, a multi-key command or transaction needs all its keys in one slot. Put the shared part in braces, `shop:{user:42}:cart` and `shop:{user:42}:wishlist`, and only for keys that are really used together, or one slot gets hot.
|
|
47
|
+
|
|
48
|
+
## 3. Caching
|
|
49
|
+
|
|
50
|
+
Default to **cache-aside**: read the cache; on a miss, read the source of truth, write the cache with a TTL, return. On a write, update the database first, then **delete** the cache key (don't write the new value into the cache from the write path, which races with concurrent readers).
|
|
51
|
+
|
|
52
|
+
Protect hot keys from a **stampede** (many requests missing at once and all hitting the database):
|
|
53
|
+
|
|
54
|
+
1. **Request coalescing in-process**: one in-flight load per key per process.
|
|
55
|
+
2. **A short rebuild lock**: on a miss, `SET lock:<key> <token> NX PX 5000`. The winner rebuilds; others wait briefly and re-read, or serve a stale copy.
|
|
56
|
+
3. **Early refresh**: store the value with a soft expiry inside it and refresh in the background shortly before the hard TTL.
|
|
57
|
+
|
|
58
|
+
Also cache "not found" for a short TTL so missing IDs don't hammer the database, and never cache errors as values. Tell the user what the staleness window is; if they need read-your-writes, the cache must be deleted in the same request that writes.
|
|
59
|
+
|
|
60
|
+
## 4. Locks
|
|
61
|
+
|
|
62
|
+
A lock in Redis is a **lease, not a guarantee**.
|
|
63
|
+
|
|
64
|
+
- Acquire: `SET lock:<resource> <random-token> NX PX <ttl-ms>`. The token is unique per acquisition.
|
|
65
|
+
- Release only your own lock: compare the token and delete in one atomic step, with a Lua script (`if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end`) or `DELEX key IFEQ token` on Redis 8.4 and later. Plain `DEL` can delete another client's lock.
|
|
66
|
+
- Replication is asynchronous, so a failover can hand the same lock to two clients. Clock jumps can too. Redlock (several independent masters) narrows this but doesn't remove it.
|
|
67
|
+
- If two holders at once would corrupt data or move money, **stop and tell the user**: protect the write itself with a fencing token checked by the database (a version column, `UPDATE ... WHERE version = ?`) or use the database's own locks. Use a Redis lock alone only where an occasional overlap is acceptable (deduplicating cron runs, rebuild locks).
|
|
68
|
+
|
|
69
|
+
## 5. Streams and pub/sub
|
|
70
|
+
|
|
71
|
+
- Pub/sub delivers only to subscribers connected at that moment, at most once. A disconnect loses messages.
|
|
72
|
+
- For jobs and events that must be processed, use a Stream: `XADD events MAXLEN ~ 100000 * field value`, a group with `XGROUP CREATE events workers $ MKSTREAM`, consumers that `XREADGROUP GROUP workers <consumer> COUNT 10 BLOCK 5000 STREAMS events >`, and `XACK` only after the work is done.
|
|
73
|
+
- Reclaim stuck messages from dead consumers with `XAUTOCLAIM` on a schedule, count deliveries, and move messages that keep failing to a dead-letter stream.
|
|
74
|
+
- Delivery is at least once, so make handlers idempotent.
|
|
75
|
+
- Cap every stream with `MAXLEN` or `MINID` trimming; an untrimmed stream grows until memory runs out.
|
|
76
|
+
|
|
77
|
+
## 6. Memory, eviction and persistence
|
|
78
|
+
|
|
79
|
+
Read `references/operations.md` before you change server settings. The short rules:
|
|
80
|
+
|
|
81
|
+
- Set `maxmemory` below the container or instance limit, leaving room for replication and AOF buffers.
|
|
82
|
+
- A pure cache uses `allkeys-lru` or `allkeys-lfu`. A store uses `noeviction` (the default) and alerts on memory. Don't mix critical keys and cache keys on one instance under a `volatile-*` policy; use two instances.
|
|
83
|
+
- A store needs persistence: AOF with `appendfsync everysec` loses at most about a second on a crash; add RDB snapshots for backups. A pure cache can run with neither.
|
|
84
|
+
- Find slow and large things with `SLOWLOG GET`, `redis-cli --bigkeys`, `redis-cli --memkeys` and `MEMORY USAGE <key>`; these are read-only.
|
|
85
|
+
|
|
86
|
+
## 7. Atomicity
|
|
87
|
+
|
|
88
|
+
- Use a single command when one exists (`INCR`, `SET NX`, `ZADD GT`, `GETDEL`).
|
|
89
|
+
- Use `MULTI`/`EXEC` to batch writes atomically. It doesn't roll back a failed command and can't branch on a value read inside it; use `WATCH` for optimistic check-and-set, or a Lua script or Function for read-then-write logic.
|
|
90
|
+
- Keep scripts short: they block every other client while they run.
|
|
91
|
+
- Pipeline independent commands to cut round trips; a pipeline is not atomic.
|
|
92
|
+
|
|
93
|
+
## Never
|
|
94
|
+
|
|
95
|
+
- Use Redis as the only copy of data the user can't lose, unless persistence and backups are set up and the user has agreed.
|
|
96
|
+
- Store secrets or personal data without a TTL and a reason.
|
|
97
|
+
- Call `KEYS`, `SMEMBERS`, `HGETALL` or `LRANGE 0 -1` on collections that can grow without bound in a request path.
|
|
98
|
+
- Retry a failed lock acquisition in a tight loop; back off with jitter.
|
|
99
|
+
|
|
100
|
+
## Report format (when reviewing)
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
Redis review: <scope>
|
|
104
|
+
Mode: cache | store | mixed (risk)
|
|
105
|
+
Findings:
|
|
106
|
+
1. [severity] <file:line or key pattern>: <problem> → <fix>
|
|
107
|
+
Settings checked: maxmemory, maxmemory-policy, appendonly/appendfsync, save
|
|
108
|
+
Not verified: <anything you could not run or see>
|
|
109
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Add a Redis cache in front of the product lookup and stop the stampede when the key expires",
|
|
4
|
+
"Our Redis instance keeps getting OOM command not allowed errors, what maxmemory-policy should we use?",
|
|
5
|
+
"Build a sliding window rate limiter for the login endpoint using Redis sorted sets",
|
|
6
|
+
"Is SET NX PX safe enough as a distributed lock for our cron jobs in Redis?",
|
|
7
|
+
"Should I use Redis Streams or pub/sub for order events that workers must not miss?",
|
|
8
|
+
"How should I name and expire keys in Valkey for per-user session data?"
|
|
9
|
+
],
|
|
10
|
+
"should_not_trigger": [
|
|
11
|
+
"Design a MongoDB schema for orders with embedded line items",
|
|
12
|
+
"Our Postgres query on the orders table is slow, can you look at the plan?",
|
|
13
|
+
"Set up a ClickHouse table for page view events partitioned by month",
|
|
14
|
+
"Write a docker-compose file that runs our API and its database",
|
|
15
|
+
"Fix the N+1 queries in our Prisma resolvers",
|
|
16
|
+
"Deploy our cache cluster with a Kubernetes StatefulSet and Helm chart"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Redis operations reference
|
|
2
|
+
|
|
3
|
+
Read this before changing `maxmemory`, eviction or persistence settings. Confirm every setting against the server's own `redis.conf` and the redis.io docs for its version; managed services (ElastiCache, Memorystore, Azure Cache, Upstash, Redis Cloud) expose only some of these, through their own console or parameter groups.
|
|
4
|
+
|
|
5
|
+
## Inspect first (read-only)
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
redis-cli INFO server # version, mode
|
|
9
|
+
redis-cli INFO memory # used_memory, maxmemory, mem_fragmentation_ratio, mem_not_counted_for_evict
|
|
10
|
+
redis-cli INFO stats # keyspace_hits, keyspace_misses, evicted_keys, expired_keys
|
|
11
|
+
redis-cli INFO persistence # rdb_last_bgsave_status, aof_enabled, aof_last_write_status
|
|
12
|
+
redis-cli CONFIG GET maxmemory*
|
|
13
|
+
redis-cli CONFIG GET appendonly
|
|
14
|
+
redis-cli CONFIG GET save
|
|
15
|
+
redis-cli SLOWLOG GET 20
|
|
16
|
+
redis-cli --bigkeys # samples with SCAN, safe on a live server but adds load
|
|
17
|
+
redis-cli --memkeys
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Hit rate: `keyspace_hits / (keyspace_hits + keyspace_misses)`. A low hit rate with high `evicted_keys` means memory is too small or the policy is wrong. A low hit rate with high `expired_keys` means TTLs are too short.
|
|
21
|
+
|
|
22
|
+
## maxmemory
|
|
23
|
+
|
|
24
|
+
- `maxmemory 0` means no limit on 64-bit builds. In a container, that means the kernel kills Redis instead of Redis evicting keys.
|
|
25
|
+
- Replication and AOF buffers are not counted toward `maxmemory`. Leave headroom: a common starting point is 70 to 80 percent of the memory available to the process, then adjust using `mem_not_counted_for_evict`.
|
|
26
|
+
- A fork for `BGSAVE` or AOF rewrite uses copy-on-write memory; on a write-heavy instance, the peak can approach double the dataset.
|
|
27
|
+
|
|
28
|
+
## maxmemory-policy
|
|
29
|
+
|
|
30
|
+
| Policy | Evicts | Use for |
|
|
31
|
+
|---|---|---|
|
|
32
|
+
| `noeviction` (default) | nothing; writes that need memory fail with an OOM error | stores, queues, anything that must not lose keys |
|
|
33
|
+
| `allkeys-lru` | least recently used, any key | general cache (good default) |
|
|
34
|
+
| `allkeys-lfu` | least frequently used, any key | cache with a stable hot set |
|
|
35
|
+
| `allkeys-random` | random | uniform access |
|
|
36
|
+
| `volatile-lru` / `volatile-lfu` / `volatile-random` | only keys with a TTL | mixed instance (prefer two instances) |
|
|
37
|
+
| `volatile-ttl` | keys with the shortest remaining TTL | app assigns short TTLs to cheap keys |
|
|
38
|
+
|
|
39
|
+
Recent Redis 8 releases add `allkeys-lrm` and `volatile-lrm` (least recently modified); check the redis.io eviction docs and release notes for the server version before using them. `volatile-*` policies behave like `noeviction` when no key has a TTL.
|
|
40
|
+
|
|
41
|
+
## Persistence
|
|
42
|
+
|
|
43
|
+
| Setting | Meaning |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `appendonly yes` | write every change to the AOF |
|
|
46
|
+
| `appendfsync everysec` | fsync once a second: lose at most about a second on power loss (usual choice) |
|
|
47
|
+
| `appendfsync always` | fsync every write: safest, slowest |
|
|
48
|
+
| `appendfsync no` | let the OS decide |
|
|
49
|
+
| `save <seconds> <changes>` | RDB snapshot rules; `save ""` disables snapshots |
|
|
50
|
+
|
|
51
|
+
- Store: AOF `everysec` plus RDB snapshots copied off the host for backups. Test a restore.
|
|
52
|
+
- Cache: persistence optional. Without it, a restart starts cold, so plan for the load on the database.
|
|
53
|
+
- Check `rdb_last_bgsave_status` and `aof_last_write_status` are `ok` after any change.
|
|
54
|
+
|
|
55
|
+
## Latency checklist
|
|
56
|
+
|
|
57
|
+
1. `SLOWLOG GET`: look for `KEYS`, `HGETALL`/`SMEMBERS` on large keys, long Lua scripts, `DEL` on big keys.
|
|
58
|
+
2. `--bigkeys`: split or page anything huge; use `UNLINK` to delete.
|
|
59
|
+
3. Network: round trips dominate small commands; pipeline or use multi-key commands.
|
|
60
|
+
4. Connections: one shared client or pool per process, not a new connection per request. Check `INFO clients` for `connected_clients` climbing.
|
|
61
|
+
5. Fork pauses: `INFO stats` `latest_fork_usec`; large datasets fork slowly.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: refactoring-safely
|
|
3
|
+
description: Step-by-step method for restructuring code while proving its observable behaviour stays identical, by pinning current output with characterisation tests, making one small reversible move at a time, preferring automated IDE-style transformations, and using a strangler fig to replace large legacy modules incrementally. Use when renaming, extracting, moving or splitting code, untangling a legacy module, or planning a rewrite that must not break existing users.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Needs git and the project's test runner.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: coding
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/requires: "git"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Refactoring safely
|
|
15
|
+
|
|
16
|
+
A refactor changes structure, never behaviour. If behaviour changes, it is a feature or a fix and goes in a separate commit.
|
|
17
|
+
|
|
18
|
+
## 0. Before you start
|
|
19
|
+
|
|
20
|
+
1. State the goal in one sentence ("split `OrderService` so pricing can be tested alone"). If you can't, ask the user what the refactor is for.
|
|
21
|
+
2. Check the working tree is clean (`git status`). If not, **stop and ask** whether to commit or stash the user's changes first.
|
|
22
|
+
3. Find and run the test command. Record the result.
|
|
23
|
+
- If tests already fail, **stop and tell the user**. Do not refactor on a red suite.
|
|
24
|
+
4. Search the codebase for every user of the code you will touch: callers, imports, reflection or string lookups, config files, templates, public API docs, other repos if it is a published package.
|
|
25
|
+
5. If the code is a public API (exported from a library, an HTTP endpoint, a CLI flag, a database column), **stop and ask** before changing its shape. Plan a deprecation instead.
|
|
26
|
+
|
|
27
|
+
## 1. Pin current behaviour (characterisation tests)
|
|
28
|
+
|
|
29
|
+
If the code you will change is not covered by tests that would fail on a behaviour change, write them first.
|
|
30
|
+
|
|
31
|
+
1. Call the code through its public entry point with realistic inputs, including edge cases: empty, null, boundary values, error paths.
|
|
32
|
+
2. Assert what it **does today**, even if it looks wrong. Note suspected bugs in a list for the user; do not fix them now.
|
|
33
|
+
3. For output-heavy code (reports, rendering, serialisers), use snapshot or golden-file tests.
|
|
34
|
+
4. Make each new test fail once on purpose (change an expected value) to prove it actually checks something, then restore it.
|
|
35
|
+
5. Commit the tests alone: "test: characterise X before refactor".
|
|
36
|
+
|
|
37
|
+
## 2. Plan the steps
|
|
38
|
+
|
|
39
|
+
Break the refactor into steps that each:
|
|
40
|
+
|
|
41
|
+
- Keep the build and tests green.
|
|
42
|
+
- Are small enough to revert with one `git revert` or `git checkout`.
|
|
43
|
+
- Change one thing: rename, or move, or extract. Not all three.
|
|
44
|
+
|
|
45
|
+
Write the step list down before starting. If it has more than about 10 steps or crosses module boundaries, show it to the user first.
|
|
46
|
+
|
|
47
|
+
## 3. Prefer mechanical refactors
|
|
48
|
+
|
|
49
|
+
Use automated transformations from the language server, IDE or a codemod tool where available: rename symbol, extract function or variable, inline, move file with import updates, change signature. They are safer than hand edits because they update every reference.
|
|
50
|
+
|
|
51
|
+
Common moves, safest first:
|
|
52
|
+
|
|
53
|
+
| Move | Watch out for |
|
|
54
|
+
|---|---|
|
|
55
|
+
| Rename | String-based lookups, serialised field names, logs others parse |
|
|
56
|
+
| Extract function or variable | Evaluation order, side effects, early returns |
|
|
57
|
+
| Inline | Expressions with side effects evaluated more or fewer times |
|
|
58
|
+
| Move to another module | Circular imports, init order, package visibility |
|
|
59
|
+
| Change signature | Default arguments, callers in other packages, mocks in tests |
|
|
60
|
+
| Replace conditional with polymorphism | Missing cases; add a test per branch first |
|
|
61
|
+
|
|
62
|
+
For hand edits, search for every reference before and after, and compare counts.
|
|
63
|
+
|
|
64
|
+
## 4. The loop
|
|
65
|
+
|
|
66
|
+
For each step:
|
|
67
|
+
|
|
68
|
+
1. Make the single change.
|
|
69
|
+
2. Run the build or type-check, then the tests for the affected area.
|
|
70
|
+
3. Green: commit with a message like "refactor: extract computeTax from Invoice". Red: **revert the step**, don't debug forward. Then take a smaller step.
|
|
71
|
+
4. Every few steps, run the full suite.
|
|
72
|
+
|
|
73
|
+
## 5. Large rewrites: strangler fig
|
|
74
|
+
|
|
75
|
+
When a module is too large to refactor in place:
|
|
76
|
+
|
|
77
|
+
1. Put a seam in front of it: an interface, a facade, a router or a proxy that all callers go through. Ship that alone, with no behaviour change.
|
|
78
|
+
2. Build the new implementation behind the same seam, one capability at a time.
|
|
79
|
+
3. Route a slice of traffic or one use case to the new code (feature flag, config switch, path-based routing).
|
|
80
|
+
4. Compare: run old and new side by side on the same inputs where you can (shadow traffic, dual-run tests) and diff the results.
|
|
81
|
+
5. When a capability matches, move all callers to it and delete the old path for that capability.
|
|
82
|
+
6. Repeat until the old module has no callers, then delete it.
|
|
83
|
+
|
|
84
|
+
Never run a big-bang rewrite that replaces everything in one release. If the user asks for one, explain the risk and offer the strangler plan.
|
|
85
|
+
|
|
86
|
+
## 6. Rules
|
|
87
|
+
|
|
88
|
+
- Never change behaviour and structure in the same commit.
|
|
89
|
+
- Never delete, skip or loosen a test to make a refactor pass. A failing test means behaviour changed.
|
|
90
|
+
- Never reformat whole files in a refactor commit; it hides the real diff. Do formatting in its own commit.
|
|
91
|
+
- Do not refactor code unrelated to the stated goal, however tempting.
|
|
92
|
+
- Keep performance-sensitive code paths' complexity the same unless the user agreed to change it.
|
|
93
|
+
|
|
94
|
+
## 7. Finish
|
|
95
|
+
|
|
96
|
+
1. Run the full test suite, linter and type-check.
|
|
97
|
+
2. Diff the start and end of the branch and check that the public API is unchanged (exports, endpoints, signatures), unless agreed.
|
|
98
|
+
3. Report:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
Refactor: <goal>
|
|
102
|
+
Steps: <n> commits, each green
|
|
103
|
+
Characterisation tests added: <files> | none needed (existing coverage: <files>)
|
|
104
|
+
Public API changed: no | yes, agreed: <what>
|
|
105
|
+
Suspected bugs found, not fixed: <list> | none
|
|
106
|
+
Checks: <commands run> -> <pass/fail>
|
|
107
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Split this 2000-line OrderService into smaller classes without breaking anything",
|
|
4
|
+
"I want to rename the User model's fields across the codebase safely",
|
|
5
|
+
"Plan how to replace our legacy billing module gradually instead of a big-bang rewrite",
|
|
6
|
+
"Add characterisation tests to this untested parser before I restructure it",
|
|
7
|
+
"Extract the pricing logic out of the controller and move it into its own module"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Find and delete dead code and unused exports in this repo",
|
|
11
|
+
"Simplify the function I just wrote, it has too much nesting",
|
|
12
|
+
"Write a failing test first, then implement the discount feature",
|
|
13
|
+
"Fix the build error after upgrading the Go version",
|
|
14
|
+
"Review my pull request for correctness bugs",
|
|
15
|
+
"Should we adopt hexagonal architecture for the new service?"
|
|
16
|
+
]
|
|
17
|
+
}
|