@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,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: kotlin-patterns
|
|
3
|
+
description: Idiomatic Kotlin for JVM and Android code, covering null safety without !!, data and sealed classes with exhaustive when, restrained extension functions, structured concurrency with coroutine scopes and dispatchers, cold Flow versus StateFlow and SharedFlow, Result and exception handling, and Gradle Kotlin DSL build scripts with version catalogs. Use when writing or restructuring Kotlin code, modelling state with sealed hierarchies, choosing a coroutine scope or dispatcher, exposing data as a Flow, or editing build.gradle.kts files.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Needs the project's Gradle wrapper (or Maven) and a JDK to build and test.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: coding
|
|
9
|
+
kete-loom/risk: writes-files
|
|
10
|
+
kete-loom/owner: "Kete-org"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Kotlin patterns
|
|
14
|
+
|
|
15
|
+
Write Kotlin that the compiler can check: nullability in the types, states in sealed hierarchies, concurrency tied to a scope. Follow the project's existing style first; these rules settle what it does not.
|
|
16
|
+
|
|
17
|
+
## 0. Before you write
|
|
18
|
+
|
|
19
|
+
1. Find the Kotlin version, JVM target and build tool: search `gradle/libs.versions.toml`, `build.gradle.kts` and `settings.gradle.kts`. Do not use language or library features newer than that version.
|
|
20
|
+
2. Check whether the code is Android (an `com.android.*` plugin is applied) or plain JVM. Android code must never block the main thread.
|
|
21
|
+
3. Find the existing conventions: error type (exceptions, `Result`, a sealed `Outcome`), DI framework, coroutine scopes in use, and the lint/format tool (ktlint, detekt, Spotless). Match them.
|
|
22
|
+
|
|
23
|
+
## 1. Null safety
|
|
24
|
+
|
|
25
|
+
- Model absence in the type (`String?`) only when absence is a real state. Otherwise make it non-null and validate at the boundary.
|
|
26
|
+
- Never use `!!` in new code. Replace it with, in order of preference:
|
|
27
|
+
1. a non-null type, so the question never arises;
|
|
28
|
+
2. `?: return`, `?: continue` or `?: throw IllegalStateException("why")` with a message;
|
|
29
|
+
3. `requireNotNull(x) { "message" }` for arguments, `checkNotNull(x) { "message" }` for state.
|
|
30
|
+
- Use `?.let { }` for a short side effect, not as a general `if`. Two nested `?.let` blocks mean an early return reads better.
|
|
31
|
+
- `lateinit var` only for values a framework sets before first use (DI fields, test setup). Never for values that can legitimately be missing.
|
|
32
|
+
- Java interop: values from unannotated Java APIs are platform types (`String!`). Give them an explicit Kotlin type at the call site (`val name: String? = javaObj.name`) so a null fails where it enters, not three calls later. For strict JSR-305 annotations, the compiler flag is `-Xjsr305=strict`.
|
|
33
|
+
|
|
34
|
+
## 2. Data, value and sealed types
|
|
35
|
+
|
|
36
|
+
- `data class` for value holders with structural equality. Keep properties `val`; use `copy()` to change them. Do not put mutable collections in them.
|
|
37
|
+
- `@JvmInline value class` to give a primitive a meaning (`UserId(val raw: String)`) without allocation.
|
|
38
|
+
- `sealed interface` (or `sealed class` when subtypes share state) for closed sets of states or results:
|
|
39
|
+
|
|
40
|
+
```kotlin
|
|
41
|
+
sealed interface LoadState<out T> {
|
|
42
|
+
data object Loading : LoadState<Nothing>
|
|
43
|
+
data class Loaded<T>(val value: T) : LoadState<T>
|
|
44
|
+
data class Failed(val cause: Throwable) : LoadState<Nothing>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fun render(state: LoadState<User>) = when (state) {
|
|
48
|
+
LoadState.Loading -> showSpinner()
|
|
49
|
+
is LoadState.Loaded -> show(state.value)
|
|
50
|
+
is LoadState.Failed -> showError(state.cause)
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- `data object` needs Kotlin 1.9 or later; on older versions use a plain `object`.
|
|
55
|
+
- Use `when` as an expression over a sealed type with no `else` branch, so adding a subtype is a compile error at every call site.
|
|
56
|
+
- `enum class` when the cases carry no per-instance data.
|
|
57
|
+
|
|
58
|
+
## 3. Functions and extensions
|
|
59
|
+
|
|
60
|
+
- Prefer expression bodies for one-expression functions, named and default arguments over overloads, and `val` over `var`.
|
|
61
|
+
- Write an extension function only when it reads as a natural operation on that type and is used in more than one place. Keep it `private` or `internal` unless it is part of a public API. Never add extensions on `Any`, `String` or other broad types for one call site.
|
|
62
|
+
- Scope functions: `apply` to configure an object, `also` for a side effect, `let` for a nullable or a transform, `run`/`with` sparingly. Do not nest them.
|
|
63
|
+
- Collections: return read-only `List`/`Map` types; keep `MutableList` private. Use `Sequence` only for long pipelines over large inputs.
|
|
64
|
+
|
|
65
|
+
## 4. Coroutines and structured concurrency
|
|
66
|
+
|
|
67
|
+
Rules that prevent leaks and freezes:
|
|
68
|
+
|
|
69
|
+
1. Every coroutine has an owner scope with a lifecycle: `viewModelScope`, `lifecycleScope`, a scope injected into a class that cancels it on shutdown, or `coroutineScope { }` inside a suspend function.
|
|
70
|
+
2. Never use `GlobalScope` in production code. Never create `CoroutineScope(Job())` in a class without a matching `cancel()`.
|
|
71
|
+
3. Suspend functions are main-safe: the function that blocks switches dispatcher itself with `withContext(Dispatchers.IO)` (blocking I/O) or `withContext(Dispatchers.Default)` (CPU work). Callers never have to know.
|
|
72
|
+
4. Inject dispatchers (constructor parameter with a default) so tests can replace them.
|
|
73
|
+
5. Never call `runBlocking` on the main thread or inside a coroutine. It is for `main()` and bridging blocking APIs at the edge.
|
|
74
|
+
6. Never swallow `CancellationException`. If you catch `Exception` or `Throwable` in suspend code, rethrow cancellation:
|
|
75
|
+
|
|
76
|
+
```kotlin
|
|
77
|
+
try { repo.sync() }
|
|
78
|
+
catch (e: CancellationException) { throw e }
|
|
79
|
+
catch (e: IOException) { log.warn("sync failed", e) }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
7. Use `coroutineScope` when one child failing should cancel siblings, and `supervisorScope` when children are independent. Parallel work: `coroutineScope { val a = async { f() }; val b = async { g() }; a.await() to b.await() }`.
|
|
83
|
+
8. Long loops must be cancellable: call `ensureActive()` or a suspending function each iteration.
|
|
84
|
+
|
|
85
|
+
## 5. Flow
|
|
86
|
+
|
|
87
|
+
- A `Flow` is cold: it runs once per collector. Expose `Flow` from repositories, `StateFlow` for UI state, `SharedFlow` for events.
|
|
88
|
+
- Keep mutable flows private: `private val _state = MutableStateFlow(UiState())` and `val state: StateFlow<UiState> = _state.asStateFlow()`. Update with `_state.update { it.copy(...) }`, never read-modify-write.
|
|
89
|
+
- Convert cold to hot with `stateIn(scope, SharingStarted.WhileSubscribed(5_000), initial)` so the upstream stops when nobody listens.
|
|
90
|
+
- Move upstream work off the main thread with `flowOn(Dispatchers.IO)`. Never call `emit` from inside `withContext` or `launch` within `flow { }`: it throws `IllegalStateException` (Flow invariant is violated). Use `flowOn`, or `channelFlow` when you need concurrent emitters. A `withContext` that returns a value you then emit outside it is fine.
|
|
91
|
+
- Collect on Android with `repeatOnLifecycle(Lifecycle.State.STARTED)` (or `collectAsStateWithLifecycle()` in Compose) so collection stops in the background.
|
|
92
|
+
- `collect` suspends until the flow ends. Two `collect` calls in a row in one coroutine means the second never runs for an infinite flow: launch each in its own coroutine.
|
|
93
|
+
- Pick the operator on purpose: `map`, `filter`, `combine`, `flatMapLatest` (cancel previous), `debounce`, `distinctUntilChanged`. Handle errors with `catch { }` upstream of `collect`.
|
|
94
|
+
|
|
95
|
+
## 6. Error handling
|
|
96
|
+
|
|
97
|
+
- Exceptions for programming errors and truly unexpected failures; `require`/`check`/`error()` for preconditions.
|
|
98
|
+
- For expected domain failures, return a sealed result type the caller must handle. Use the project's existing type if there is one.
|
|
99
|
+
- `runCatching` also catches `CancellationException`; do not use it around suspend calls unless you rethrow cancellation.
|
|
100
|
+
- Never catch and ignore. Log with context or convert to a domain error.
|
|
101
|
+
|
|
102
|
+
## 7. Gradle Kotlin DSL
|
|
103
|
+
|
|
104
|
+
- Keep versions in `gradle/libs.versions.toml` and reference them as `libs.foo`, `alias(libs.plugins.foo)`. Do not hard-code versions in module scripts.
|
|
105
|
+
- Set the JDK with `kotlin { jvmToolchain(17) }` (use the project's version) rather than `sourceCompatibility` alone.
|
|
106
|
+
- Compiler options go in `kotlin { compilerOptions { ... } }`, for example `freeCompilerArgs.add("-Xjsr305=strict")` or `allWarningsAsErrors.set(true)`.
|
|
107
|
+
- Shared build logic goes in convention plugins (`build-logic/` or `buildSrc/`), not copy-pasted blocks.
|
|
108
|
+
- Prefer KSP (`com.google.devtools.ksp`, `ksp(...)`) over kapt for processors that support it.
|
|
109
|
+
- Keep the Kotlin Gradle plugin, KSP, Compose compiler and Android Gradle Plugin versions compatible; check the official compatibility tables before bumping one.
|
|
110
|
+
- Run builds through the wrapper: `./gradlew build`.
|
|
111
|
+
|
|
112
|
+
## 8. Check your work
|
|
113
|
+
|
|
114
|
+
1. Build and test: `./gradlew build` (or the module task, e.g. `./gradlew :app:testDebugUnitTest`).
|
|
115
|
+
2. Run the project's linter if configured (`./gradlew ktlintCheck`, `./gradlew detekt`, `./gradlew spotlessCheck`).
|
|
116
|
+
3. Search your diff for `!!`, `GlobalScope`, `runBlocking` and `catch (e: Exception)` and justify or remove each.
|
|
117
|
+
|
|
118
|
+
## Stop and ask the user
|
|
119
|
+
|
|
120
|
+
- Before changing a public API signature other modules or apps call.
|
|
121
|
+
- Before bumping the Kotlin, AGP or Gradle version to use a feature.
|
|
122
|
+
- When the project mixes error styles and you would have to pick one for a new module.
|
|
123
|
+
|
|
124
|
+
## Report back
|
|
125
|
+
|
|
126
|
+
List the files changed, the build and lint commands you ran with their result, and any `!!`, `runBlocking` or broad catch you kept, with the reason.
|
|
127
|
+
|
|
128
|
+
For tests, use the `kotlin-testing` skill. For a review of existing changes, the `kotlin-reviewer` agent. For compile or Gradle failures, the `kotlin-build-resolver` agent.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Model the checkout screen state in Kotlin as a sealed interface with loading, success and error cases",
|
|
4
|
+
"This Kotlin repository class has !! everywhere, rewrite it with proper null safety",
|
|
5
|
+
"Which dispatcher should my suspend function use for the database call, and who should own the coroutine scope?",
|
|
6
|
+
"Expose the user list from my Android ViewModel as a StateFlow instead of LiveData",
|
|
7
|
+
"Move our hard-coded dependency versions in build.gradle.kts into a libs.versions.toml catalog"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Add a test that checks my Kotlin repository retries twice before failing",
|
|
11
|
+
"Go through the Kotlin PR my teammate opened and flag any risky coroutine code",
|
|
12
|
+
"The Android build broke with a Duplicate class error after adding a library",
|
|
13
|
+
"Build the settings screen in Jetpack Compose with a lazy list",
|
|
14
|
+
"Share the networking layer between Android and iOS with Kotlin Multiplatform"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: kotlin-testing
|
|
3
|
+
description: Writes and repairs Kotlin tests on JUnit 5 or Kotest, with MockK for coroutine-aware mocks, kotlinx-coroutines-test for runTest, virtual time and Dispatchers.setMain, and Turbine for asserting Flow and StateFlow emissions, plus a checklist for making flaky coroutine tests deterministic. Use when adding unit tests to Kotlin or Android code, testing suspend functions or ViewModels, asserting what a Flow emits, mocking with every and coEvery, or a Kotlin test passes locally but fails or hangs intermittently in CI.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Needs a JDK and the project's Gradle wrapper (or Maven) to run tests.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: testing
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/owner: "Kete-org"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Kotlin testing
|
|
14
|
+
|
|
15
|
+
Tests that run in milliseconds, never touch real time or the network, and fail for one reason.
|
|
16
|
+
|
|
17
|
+
## 0. Find the setup
|
|
18
|
+
|
|
19
|
+
1. Search the build files for the framework already in use: `junit-jupiter` (JUnit 5), `kotest-runner-junit5` (Kotest), `junit:junit` (JUnit 4, common in Android). Use what is there. Do not add a second framework.
|
|
20
|
+
2. Check for `mockk`, `kotlinx-coroutines-test` and `app.cash.turbine:turbine`. If a test needs one that is missing, stop and ask the user before adding it, then add it as a `testImplementation` dependency (through `gradle/libs.versions.toml` if the project uses a version catalog).
|
|
21
|
+
3. JUnit 5 on Gradle needs `tasks.test { useJUnitPlatform() }` (Kotest runs on the same platform). If tests are silently not running, check this first.
|
|
22
|
+
4. Find the test command: `./gradlew test`, or for Android `./gradlew :module:testDebugUnitTest`. Run one class with `--tests "com.example.FooTest"`.
|
|
23
|
+
|
|
24
|
+
## 1. Test shape
|
|
25
|
+
|
|
26
|
+
- One behaviour per test. Name it for the behaviour: JUnit 5 backtick names (`` fun `returns cached user when offline`() ``) or Kotest `test("...")`.
|
|
27
|
+
- Arrange, act, assert, with a blank line between. Build inputs with small factory functions with defaults, not shared mutable fixtures.
|
|
28
|
+
- Assert on values, not on calls, wherever you can. Use `assertEquals` (kotlin.test or JUnit) or Kotest matchers (`shouldBe`, `shouldThrow<T> { }`).
|
|
29
|
+
- Prefer a hand-written fake (an in-memory implementation of the interface) over a mock for repositories and data sources. Mock only what you cannot fake cheaply.
|
|
30
|
+
|
|
31
|
+
## 2. MockK
|
|
32
|
+
|
|
33
|
+
```kotlin
|
|
34
|
+
val api = mockk<UserApi>()
|
|
35
|
+
coEvery { api.fetch("42") } returns User("42", "Ama") // suspend function
|
|
36
|
+
every { clock.now() } returns Instant.parse("2026-01-01T00:00:00Z")
|
|
37
|
+
|
|
38
|
+
// act ...
|
|
39
|
+
|
|
40
|
+
coVerify(exactly = 1) { api.fetch("42") }
|
|
41
|
+
confirmVerified(api)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- `every`/`verify` for regular functions, `coEvery`/`coVerify` for suspend functions. Mixing them up is the most common MockK error.
|
|
45
|
+
- Use strict mocks by default; `mockk(relaxed = true)` hides missing stubs. Use `relaxUnitFun = true` when you only want `Unit` functions relaxed.
|
|
46
|
+
- Capture arguments with `val slot = slot<Request>()` and `coEvery { api.send(capture(slot)) }`.
|
|
47
|
+
- `mockkObject`, `mockkStatic` and `mockkConstructor` change global state: call `unmockkAll()` in teardown, or avoid them by injecting the dependency.
|
|
48
|
+
- Never mock the class under test or data classes.
|
|
49
|
+
|
|
50
|
+
## 3. Coroutines: runTest
|
|
51
|
+
|
|
52
|
+
```kotlin
|
|
53
|
+
@Test
|
|
54
|
+
fun `loads user`() = runTest {
|
|
55
|
+
val repo = UserRepository(api, ioDispatcher = StandardTestDispatcher(testScheduler))
|
|
56
|
+
val user = repo.load("42")
|
|
57
|
+
assertEquals("Ama", user.name)
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- Wrap every test that calls suspend code in `runTest`. It skips `delay` with virtual time, so a 30-second timeout test finishes instantly.
|
|
62
|
+
- Inject dispatchers into production code. In tests, pass a `StandardTestDispatcher(testScheduler)` (or `UnconfinedTestDispatcher(testScheduler)` when you want eager execution) so all coroutines share one scheduler. A hard-coded `Dispatchers.IO` escapes virtual time and makes tests flaky.
|
|
63
|
+
- Code that uses `Dispatchers.Main` (ViewModels): call `Dispatchers.setMain(testDispatcher)` before each test and `Dispatchers.resetMain()` after. Put it in a JUnit 5 extension or a JUnit 4 rule the project can reuse.
|
|
64
|
+
- Control time with `advanceUntilIdle()`, `advanceTimeBy(ms)` and `runCurrent()`. Read virtual time with `currentTime`.
|
|
65
|
+
- Launch long-running collectors in `backgroundScope` so `runTest` cancels them at the end instead of waiting for them until its timeout fails the test.
|
|
66
|
+
- Never use `runBlocking` or `Thread.sleep` in tests of coroutine code.
|
|
67
|
+
|
|
68
|
+
## 4. Flow: Turbine
|
|
69
|
+
|
|
70
|
+
```kotlin
|
|
71
|
+
@Test
|
|
72
|
+
fun `emits loading then loaded`() = runTest {
|
|
73
|
+
viewModel.state.test {
|
|
74
|
+
assertEquals(UiState.Loading, awaitItem())
|
|
75
|
+
assertEquals(UiState.Loaded(user), awaitItem())
|
|
76
|
+
cancelAndIgnoreRemainingEvents()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- `test { }` fails if events are left unconsumed. Finish with `awaitComplete()`, `awaitError()` or `cancelAndIgnoreRemainingEvents()`.
|
|
82
|
+
- `StateFlow` conflates and replays its current value: the first `awaitItem()` is the current state, and fast successive updates can merge. Assert the final state with `expectMostRecentItem()` when intermediate states do not matter.
|
|
83
|
+
- Several flows at once: `turbineScope { val a = flowA.testIn(backgroundScope); ... }`, and end each with `cancel()`, `awaitComplete()` or `awaitError()`.
|
|
84
|
+
- Turbine's wait timeout is wall-clock (default 3 seconds), not virtual time. A timeout usually means the code under test never emitted because its dispatcher was not advanced.
|
|
85
|
+
- Without Turbine, collect a finite flow with `flow.toList()` or `flow.take(n).toList()`.
|
|
86
|
+
|
|
87
|
+
## 5. Kotest specifics
|
|
88
|
+
|
|
89
|
+
- Pick one spec style per project (`FunSpec`, `StringSpec`, `BehaviorSpec`) and match it.
|
|
90
|
+
- Kotest test bodies are already suspend functions; you can call suspend code directly. Use `runTest` only when you need virtual time, or the project's coroutine test extension.
|
|
91
|
+
- Use `beforeTest`/`afterTest` for setup and `isolationMode` deliberately: the default shares one spec instance across tests.
|
|
92
|
+
|
|
93
|
+
## 6. Fixing flaky tests
|
|
94
|
+
|
|
95
|
+
Work through this list in order, and run the test 20 times after each change (`./gradlew test --tests "X" --rerun-tasks` in a loop, or a repeated-test annotation) to prove it:
|
|
96
|
+
|
|
97
|
+
1. Real dispatcher in production code (`Dispatchers.IO`/`Default` hard-coded): inject it and pass the test dispatcher.
|
|
98
|
+
2. Multiple `TestCoroutineScheduler`s: every test dispatcher must share `testScheduler`.
|
|
99
|
+
3. Missing `Dispatchers.setMain` or a missing `resetMain` leaking between tests.
|
|
100
|
+
4. Real time: `System.currentTimeMillis()`, `Instant.now()`, `Thread.sleep`. Inject a clock.
|
|
101
|
+
5. Shared state: companion objects, singletons, `mockkObject` without `unmockkAll`, test order dependence.
|
|
102
|
+
6. StateFlow conflation making an intermediate `awaitItem()` miss a value.
|
|
103
|
+
7. Real network, filesystem or database. Replace with a fake or a temp directory.
|
|
104
|
+
|
|
105
|
+
Never "fix" a flaky test by adding `delay`, retries or a longer timeout.
|
|
106
|
+
|
|
107
|
+
## Rules
|
|
108
|
+
|
|
109
|
+
- Never delete, `@Disabled` or weaken an existing test to make the suite pass without the user's agreement.
|
|
110
|
+
- No real network calls, no real clock, no sleeps.
|
|
111
|
+
- Run the single test, then the whole module's tests, before you finish.
|
|
112
|
+
|
|
113
|
+
## Report back
|
|
114
|
+
|
|
115
|
+
Tests added or fixed (class and test names), the exact command you ran, the pass/fail counts, and for a flaky test the root cause from section 6 and how many repeated runs passed.
|
|
116
|
+
|
|
117
|
+
For writing the production code itself, use the `kotlin-patterns` skill. For a test-first loop in any language, `tdd-workflow`.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Write unit tests for my Kotlin ViewModel using runTest and Turbine",
|
|
4
|
+
"How do I mock a suspend function with MockK? coEvery doesn't seem to work",
|
|
5
|
+
"My Kotest spec hangs forever when the repository uses delay()",
|
|
6
|
+
"This coroutine test passes on my laptop but fails randomly in CI, help me make it deterministic",
|
|
7
|
+
"Assert that my StateFlow emits Loading and then Success in a JUnit 5 test"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Rewrite this Kotlin service to use structured concurrency instead of GlobalScope",
|
|
11
|
+
"Review my Kotlin diff for Flow collection bugs and !! usage",
|
|
12
|
+
"kaptDebugKotlin fails after adding Room, fix the build",
|
|
13
|
+
"Write pytest fixtures for my FastAPI endpoints",
|
|
14
|
+
"Write a Playwright end-to-end test for the login page"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ktor-patterns
|
|
3
|
+
description: Kotlin Ktor server guidance for Application modules, the routing DSL, installing ContentNegotiation with kotlinx.serialization, Authentication (JWT, bearer, sessions), StatusPages error mapping, structured coroutines and Dispatchers.IO for blocking work, wiring dependencies with Ktor DI or Koin, and in-process tests with testApplication. Use when writing or refactoring a Ktor backend, adding routes or plugins, fixing Ktor serialization or auth errors, or testing Ktor endpoints.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Needs a JDK and the project's Gradle (or Maven) wrapper. Written for Ktor 3.x; the built-in DI plugin needs a recent 3.x release.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: coding
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/requires: "java"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Ktor server patterns
|
|
15
|
+
|
|
16
|
+
Ktor gives you a small core and explicit plugins. Keep it that way: every behaviour is installed in code you can read, and every module can be started in a test.
|
|
17
|
+
|
|
18
|
+
## 0. Before you start
|
|
19
|
+
|
|
20
|
+
1. Find the Ktor version (`io.ktor` dependencies or `libs.versions.toml`) and the engine (`ktor-server-netty`, `-cio`, `-jetty`). Ktor 2 and 3 differ in some APIs; follow the version the project uses.
|
|
21
|
+
2. Find how the app starts: `embeddedServer(...)` in `main`, or `EngineMain` with `application.conf`/`application.yaml` listing `ktor.application.modules`.
|
|
22
|
+
3. Run `./gradlew test` once and record the result. If it already fails, **stop and tell the user**.
|
|
23
|
+
|
|
24
|
+
## 1. Modules and structure
|
|
25
|
+
|
|
26
|
+
- Split setup into extension functions on `Application`: `fun Application.configureSerialization()`, `configureSecurity()`, `configureRouting()`. The main `module()` calls them in order.
|
|
27
|
+
- Keep routes in feature files as extension functions on `Route` (`fun Route.orderRoutes(service: OrderService)`), registered inside `routing { }`.
|
|
28
|
+
- Routes are thin: read parameters, validate, call a service, respond. Business logic and database access live in services and repositories that don't import Ktor.
|
|
29
|
+
- Read config from `environment.config` (`property("payments.baseUrl").getString()`) or env vars, never hard-coded. Secrets come from the environment.
|
|
30
|
+
|
|
31
|
+
## 2. Routing
|
|
32
|
+
|
|
33
|
+
- Group with `route("/orders") { get { } ; get("{id}") { } ; post { } }`.
|
|
34
|
+
- Read path and query parameters defensively: `call.parameters["id"]?.toLongOrNull() ?: return@get call.respond(HttpStatusCode.BadRequest)`. Better still, throw a domain exception and map it in StatusPages.
|
|
35
|
+
- For typed routes, the `Resources` plugin (`@Resource` classes) gives compile-checked paths; use it if the project already does.
|
|
36
|
+
- `call.receive<CreateOrder>()` throws on malformed bodies; let StatusPages turn that into 400.
|
|
37
|
+
- Respond with DTOs annotated `@Serializable`, never database rows or internal models.
|
|
38
|
+
|
|
39
|
+
## 3. Plugins
|
|
40
|
+
|
|
41
|
+
Install each plugin once, at application level unless it must be scoped to a route.
|
|
42
|
+
|
|
43
|
+
**ContentNegotiation**
|
|
44
|
+
|
|
45
|
+
```kotlin
|
|
46
|
+
install(ContentNegotiation) {
|
|
47
|
+
json(Json { ignoreUnknownKeys = true; explicitNulls = false })
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Needs `ktor-server-content-negotiation`, `ktor-serialization-kotlinx-json` and the `kotlin("plugin.serialization")` Gradle plugin. A missing `@Serializable` or missing compiler plugin is the usual cause of "Serializer for class ... is not found".
|
|
52
|
+
|
|
53
|
+
**StatusPages**
|
|
54
|
+
|
|
55
|
+
```kotlin
|
|
56
|
+
install(StatusPages) {
|
|
57
|
+
exception<NotFoundException> { call, e -> call.respond(HttpStatusCode.NotFound, ErrorBody(e.message)) }
|
|
58
|
+
exception<BadRequestException> { call, _ -> call.respond(HttpStatusCode.BadRequest, ErrorBody("Invalid request")) }
|
|
59
|
+
exception<Throwable> { call, e ->
|
|
60
|
+
call.application.log.error("Unhandled", e)
|
|
61
|
+
call.respond(HttpStatusCode.InternalServerError, ErrorBody("Internal error"))
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Map each domain exception to one status. The catch-all logs and returns a generic body; never send `e.message` or stack traces from unexpected errors to clients.
|
|
67
|
+
|
|
68
|
+
**Authentication**
|
|
69
|
+
|
|
70
|
+
```kotlin
|
|
71
|
+
install(Authentication) {
|
|
72
|
+
jwt("auth-jwt") {
|
|
73
|
+
realm = "api"
|
|
74
|
+
verifier(JWT.require(Algorithm.HMAC256(secret)).withIssuer(issuer).withAudience(audience).build())
|
|
75
|
+
validate { cred -> if (cred.payload.subject != null) JWTPrincipal(cred.payload) else null }
|
|
76
|
+
challenge { _, _ -> call.respond(HttpStatusCode.Unauthorized) }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
routing { authenticate("auth-jwt") { orderRoutes(service) } }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
- Everything that isn't public goes inside `authenticate(...)`. Read the user with `call.principal<JWTPrincipal>()` and check ownership in the service.
|
|
83
|
+
- Always verify issuer, audience and expiry. Prefer RS256 with a JWKS provider (`jwt { verifier(jwkProvider, issuer) }`) when tokens come from an identity provider.
|
|
84
|
+
- **Stop and ask the user** before changing which routes are public or how tokens are verified.
|
|
85
|
+
|
|
86
|
+
Other common plugins: `CallLogging` (with `callIdMdc`), `CallId`, `CORS` (list origins explicitly; never `anyHost()` in production), `Compression`, `DefaultHeaders`.
|
|
87
|
+
|
|
88
|
+
## 4. Coroutines
|
|
89
|
+
|
|
90
|
+
- Route handlers are `suspend` and run on the engine's threads. **Never block them**: wrap JDBC, file I/O and blocking SDK calls in `withContext(Dispatchers.IO) { }`, or use a non-blocking client.
|
|
91
|
+
- Never call `runBlocking` inside a handler or service.
|
|
92
|
+
- Don't start work with `GlobalScope.launch`. For fire-and-forget work tied to the app, launch in the `Application` scope (`application.launch { }`) so it is cancelled on shutdown; for durable work use a queue.
|
|
93
|
+
- Let `CancellationException` propagate. Don't swallow it in a `catch (e: Exception)`; rethrow it.
|
|
94
|
+
- Set timeouts on outbound calls (`HttpTimeout` in the Ktor client) so a slow dependency can't hold requests open.
|
|
95
|
+
|
|
96
|
+
## 5. Dependency injection
|
|
97
|
+
|
|
98
|
+
Pick one approach and keep it:
|
|
99
|
+
|
|
100
|
+
- **Manual wiring** (fine for small services): build services in `module()` and pass them into route functions.
|
|
101
|
+
- **Ktor DI plugin** (`ktor-server-di`): register with `dependencies { provide<OrderService>(::OrderServiceImpl) }` (constructor parameters are resolved by type) and resolve with `val service: OrderService by dependencies` or `dependencies.resolve<OrderService>()`.
|
|
102
|
+
- **Koin** (`koin-ktor`): `install(Koin) { modules(appModule) }` and `val service by inject<OrderService>()` in routes. Use it when the project already does.
|
|
103
|
+
|
|
104
|
+
Whatever you use, services must be constructible in a test without a running server.
|
|
105
|
+
|
|
106
|
+
## 6. Testing with testApplication
|
|
107
|
+
|
|
108
|
+
```kotlin
|
|
109
|
+
class OrderRoutesTest {
|
|
110
|
+
@Test
|
|
111
|
+
fun `returns 404 for unknown order`() = testApplication {
|
|
112
|
+
application { module(orderService = FakeOrderService()) }
|
|
113
|
+
val client = createClient { install(ContentNegotiation) { json() } }
|
|
114
|
+
val response = client.get("/orders/999") { bearerAuth(testToken()) }
|
|
115
|
+
assertEquals(HttpStatusCode.NotFound, response.status)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
- `testApplication` runs the app in process with no real network. Load your real modules with `application { }` so the plugins under test are the real ones; with `EngineMain`, set config via `environment { config = ApplicationConfig("application-test.conf") }`.
|
|
121
|
+
- The client in `createClient` is a Ktor client; install client `ContentNegotiation` to send and read JSON bodies.
|
|
122
|
+
- Replace external dependencies with fakes passed into the module, or with test registrations in your DI setup. Use Testcontainers for real database tests.
|
|
123
|
+
- Cover for each route: success, validation failure (400), unauthenticated (401), wrong owner (403 or 404), not found, and the StatusPages mapping.
|
|
124
|
+
- Use `externalServices { }` to stub third-party HTTP APIs the app calls.
|
|
125
|
+
|
|
126
|
+
## Never
|
|
127
|
+
|
|
128
|
+
- Blocking I/O on request threads, `runBlocking` in handlers, or `GlobalScope`.
|
|
129
|
+
- Returning exception messages from the catch-all handler.
|
|
130
|
+
- `anyHost()` CORS, or protected routes outside `authenticate { }`.
|
|
131
|
+
- Installing the same plugin twice (Ktor throws `DuplicatePluginException`).
|
|
132
|
+
|
|
133
|
+
## Before you finish
|
|
134
|
+
|
|
135
|
+
Run `./gradlew test` (and `./gradlew build` if there are other checks). Report: routes added or changed with their auth requirement, plugins installed or reconfigured, tests added, and the final test counts.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Add JWT authentication and StatusPages error handling to my Ktor server",
|
|
4
|
+
"Ktor says Serializer for class OrderDto is not found when I call call.respond",
|
|
5
|
+
"Write tests for our Ktor routes with testApplication and a fake repository",
|
|
6
|
+
"Set up the Ktor DI plugin or Koin to inject services into Ktor routing",
|
|
7
|
+
"Is it ok to call JDBC directly inside a Ktor route handler or should I switch dispatchers?",
|
|
8
|
+
"Split my giant Ktor Application.module into routing, security and serialization plugins"
|
|
9
|
+
],
|
|
10
|
+
"should_not_trigger": [
|
|
11
|
+
"Build a Quarkus REST endpoint backed by Panache and make it compile to a native image",
|
|
12
|
+
"Bind the payments.* settings in application.yml to a typed config class in Spring Boot",
|
|
13
|
+
"Explain Kotlin sealed classes and Flow operators for our Android app",
|
|
14
|
+
"Share networking code between Android and iOS with Kotlin Multiplatform and the Ktor client",
|
|
15
|
+
"Review this Kotlin pull request for coroutine misuse",
|
|
16
|
+
"Write unit tests with MockK and kotest for our Kotlin pricing module"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: kubernetes-patterns
|
|
3
|
+
description: Kubernetes workload manifests and day-two operations with kubectl, covering Deployments, startup, readiness and liveness probes, CPU and memory requests and limits, ConfigMaps and Secrets, rolling updates and rollbacks, HorizontalPodAutoscaler, PodDisruptionBudgets and a step-by-step pod debugging routine. Use when writing or fixing Deployment, Service or HPA YAML, a rollout is stuck or needs undoing, or pods are in CrashLoopBackOff, Pending, OOMKilled or ImagePullBackOff.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Cluster commands need kubectl configured for the target cluster; Helm or Kustomize only if the project already uses them.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: devops
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/requires: "kubectl"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Kubernetes patterns
|
|
15
|
+
|
|
16
|
+
Write workloads that start reliably, roll out without downtime, can be rolled back, and tell you why they are failing. Building the container image belongs to `docker-patterns`; provisioning the cluster itself belongs to `terraform-patterns`.
|
|
17
|
+
|
|
18
|
+
## 0. Before touching anything
|
|
19
|
+
|
|
20
|
+
1. Find how manifests are managed: plain YAML, Kustomize (`kustomization.yaml`), Helm (`Chart.yaml`, `values*.yaml`) or a GitOps tool (Argo CD, Flux). Edit the source of truth, never the live object, when GitOps is in use: a `kubectl edit` will be reverted.
|
|
21
|
+
2. Run `kubectl config current-context` and say which cluster and namespace you are on.
|
|
22
|
+
3. **Stop and ask the user before any write to a cluster** (`apply`, `delete`, `rollout undo`, `scale`, `edit`, `patch`), and always before anything in a context or namespace that looks like production. Read-only commands (`get`, `describe`, `logs`, `events`, `top`, `diff`) are fine.
|
|
23
|
+
4. Prefer `kubectl diff -f <file>` before `kubectl apply -f <file>`, and show the diff.
|
|
24
|
+
|
|
25
|
+
## 1. Deployment checklist
|
|
26
|
+
|
|
27
|
+
Every long-running workload gets all of these. A full annotated example is in `references/manifests.md`.
|
|
28
|
+
|
|
29
|
+
1. **Labels**: a stable `selector.matchLabels` (it is immutable after creation) and the same labels on the pod template. Use `app.kubernetes.io/name` and `app.kubernetes.io/instance`.
|
|
30
|
+
2. **Image** pinned to an immutable tag or digest, never `latest`. `imagePullPolicy: IfNotPresent` for immutable tags.
|
|
31
|
+
3. **Replicas** at least 2 for anything user-facing, unless an HPA owns the count. When an HPA manages the Deployment, remove `replicas` from the manifest so an apply does not reset it.
|
|
32
|
+
4. **Resources**: set `requests` for CPU and memory on every container, and a memory `limit`. Memory over the limit gets the container OOMKilled. A CPU limit throttles rather than kills; set one only if the team's policy requires it, and know it can add latency.
|
|
33
|
+
5. **Probes**, each with a distinct job:
|
|
34
|
+
- `startupProbe` for slow starters: holds off the other probes until it passes. Size `failureThreshold × periodSeconds` to the worst startup time.
|
|
35
|
+
- `readinessProbe`: "can I take traffic now". Failing it removes the pod from Service endpoints; it does not restart it. It may check critical dependencies.
|
|
36
|
+
- `livenessProbe`: "am I stuck and need a restart". Keep it cheap and local; never check the database or another service here, or one outage restarts every pod.
|
|
37
|
+
6. **Graceful shutdown**: the app handles `SIGTERM`, and `terminationGracePeriodSeconds` covers in-flight requests. A short `preStop` sleep (a few seconds) helps load balancers drain, if the image has a `sleep` binary.
|
|
38
|
+
7. **Security context**: `runAsNonRoot: true`, `allowPrivilegeEscalation: false`, `readOnlyRootFilesystem: true` where the app allows (mount an `emptyDir` for `/tmp`), `capabilities.drop: ["ALL"]`, `seccompProfile.type: RuntimeDefault`.
|
|
39
|
+
8. **Rollout strategy**: `RollingUpdate` with `maxUnavailable: 0` and `maxSurge: 1` (or 25%) for zero downtime. Set `revisionHistoryLimit` so rollbacks are possible.
|
|
40
|
+
9. **PodDisruptionBudget** for workloads with 2+ replicas, so node drains keep some pods up. `minAvailable` must be lower than the replica count, or drains block forever.
|
|
41
|
+
10. **ServiceAccount**: a dedicated one, with `automountServiceAccountToken: false` unless the app calls the API.
|
|
42
|
+
|
|
43
|
+
## 2. Configuration and secrets
|
|
44
|
+
|
|
45
|
+
- Non-secret settings in a ConfigMap, consumed via `envFrom` or a mounted file.
|
|
46
|
+
- Credentials in a Secret. A Secret is base64-encoded, not encrypted: never commit a plain Secret manifest with real values. Use what the project already has (External Secrets Operator, Sealed Secrets, SOPS, the cloud's secret store CSI driver) and stop and ask if there is nothing.
|
|
47
|
+
- Pods do not restart when a ConfigMap or Secret used as env changes. Trigger a rollout (`kubectl rollout restart deployment/<name>`), or use a checksum annotation in Helm or Kustomize's `configMapGenerator` hash suffix.
|
|
48
|
+
|
|
49
|
+
## 3. Rollouts and rollbacks
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
kubectl rollout status deployment/<name> -n <ns> --timeout=5m
|
|
53
|
+
kubectl rollout history deployment/<name> -n <ns>
|
|
54
|
+
kubectl rollout undo deployment/<name> -n <ns> # previous revision
|
|
55
|
+
kubectl rollout undo deployment/<name> -n <ns> --to-revision=N
|
|
56
|
+
kubectl rollout pause|resume deployment/<name> -n <ns>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- Set `progressDeadlineSeconds` so a stuck rollout reports `ProgressDeadlineExceeded` instead of hanging.
|
|
60
|
+
- With GitOps, roll back by reverting the commit; `rollout undo` will be overwritten on the next sync.
|
|
61
|
+
- A rollback does not undo database migrations. Check before rolling back past a schema change.
|
|
62
|
+
|
|
63
|
+
## 4. Autoscaling
|
|
64
|
+
|
|
65
|
+
- Use `autoscaling/v2` HorizontalPodAutoscaler. CPU-utilisation targets are a percentage of the **request**, so requests must be set.
|
|
66
|
+
- Needs metrics-server (or a custom metrics adapter); check `kubectl top pods` works first.
|
|
67
|
+
- Set `minReplicas` ≥ 2 for availability, and a `behavior.scaleDown.stabilizationWindowSeconds` to avoid flapping.
|
|
68
|
+
- Don't run an HPA and a VerticalPodAutoscaler in update mode on the same CPU or memory metric.
|
|
69
|
+
|
|
70
|
+
## 5. Debugging a failing pod
|
|
71
|
+
|
|
72
|
+
Work in this order and stop when you find the cause:
|
|
73
|
+
|
|
74
|
+
1. `kubectl get pods -n <ns> -o wide`: status, restarts, node.
|
|
75
|
+
2. `kubectl describe pod <pod> -n <ns>`: read **Events** at the bottom and each container's **Last State** (reason, exit code).
|
|
76
|
+
3. `kubectl logs <pod> -n <ns> -c <container>` and `--previous` for the crashed instance.
|
|
77
|
+
4. `kubectl get events -n <ns> --sort-by=.lastTimestamp`.
|
|
78
|
+
5. If the image has no shell: `kubectl debug -it <pod> -n <ns> --image=busybox --target=<container>`. This adds an ephemeral container to the live pod, so it is a cluster write: ask first.
|
|
79
|
+
|
|
80
|
+
Common causes:
|
|
81
|
+
|
|
82
|
+
| Symptom | Look at |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `Pending` | Events: insufficient CPU/memory, unbound PVC, node selector/taints/affinity with no match |
|
|
85
|
+
| `ImagePullBackOff` / `ErrImagePull` | Image name or tag typo, private registry without `imagePullSecrets`, wrong architecture |
|
|
86
|
+
| `CrashLoopBackOff` | `logs --previous`; bad config or missing env var; liveness probe killing a slow start (add a startupProbe) |
|
|
87
|
+
| `OOMKilled` (exit 137) | Memory limit too low for real usage, or a leak; check `kubectl top pod` |
|
|
88
|
+
| Running but no traffic | Readiness failing, Service `selector` not matching pod labels, wrong `targetPort`; check `kubectl get endpointslices -l kubernetes.io/service-name=<svc>` |
|
|
89
|
+
| `CreateContainerConfigError` | Referenced ConfigMap/Secret or key does not exist |
|
|
90
|
+
|
|
91
|
+
Report the cause with the exact event or log line that proves it, before proposing a fix.
|
|
92
|
+
|
|
93
|
+
## 6. Verify
|
|
94
|
+
|
|
95
|
+
- Validate before applying: `kubectl apply --dry-run=client -f <file>` offline, or `kubectl apply --dry-run=server -f <file>` (needs cluster access; runs admission checks without persisting). If the project uses `kubeconform`, `helm lint` or `helm template`, or `kustomize build`, run them.
|
|
96
|
+
- After an approved apply: `kubectl rollout status` reaches success, pods are `Ready`, and the Service has endpoints.
|
|
97
|
+
|
|
98
|
+
## Never
|
|
99
|
+
|
|
100
|
+
- Never `kubectl delete` a namespace, PVC or StatefulSet, or `--force --grace-period=0` a pod, without explicit user approval.
|
|
101
|
+
- Never put secret values in a ConfigMap, a manifest committed to git, or a command line that ends up in shell history.
|
|
102
|
+
- Never set `privileged: true`, `hostNetwork`, `hostPID` or a `hostPath` mount unless the user asks and states why.
|
|
103
|
+
|
|
104
|
+
## Report back
|
|
105
|
+
|
|
106
|
+
End with: cluster context and namespace used, files changed, commands run (marking which ones wrote to the cluster and that the user approved them), the rollout or debug result with the evidence line, and what you did not check.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Our api pods keep going into CrashLoopBackOff after the deploy, help me find out why",
|
|
4
|
+
"Write a Deployment and Service manifest for the web app with readiness and liveness probes",
|
|
5
|
+
"The new release is broken, how do I roll the checkout deployment back to the last revision?",
|
|
6
|
+
"Set up a HorizontalPodAutoscaler for the worker so it scales on CPU",
|
|
7
|
+
"Pods are stuck in Pending and kubectl describe mentions insufficient memory"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Make our Dockerfile smaller and stop it running as root",
|
|
11
|
+
"Provision an EKS cluster and its node groups with Terraform",
|
|
12
|
+
"Audit our k8s manifests and Terraform for security misconfigurations and report findings",
|
|
13
|
+
"Plan a blue-green release process for the mobile backend",
|
|
14
|
+
"Our docker compose app container can't reach the database on localhost"
|
|
15
|
+
]
|
|
16
|
+
}
|