@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,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jetpack-compose-patterns
|
|
3
|
+
description: Android Jetpack Compose UI work centred on hoisting state into stateless composables, understanding recomposition, stability and strong skipping, choosing the right side-effect API (LaunchedEffect, DisposableEffect, rememberUpdatedState, derivedStateOf, snapshotFlow), exposing ViewModel state with collectAsStateWithLifecycle, type-safe Navigation Compose routes, and lazy-list and baseline-profile performance. Use when writing or restructuring Compose screens on Android, debugging excessive recomposition or janky LazyColumn scrolling, wiring a ViewModel to a composable, or setting up Compose navigation.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Needs a JDK and the project's Gradle wrapper (./gradlew); device runs need the Android SDK and an emulator or device.
|
|
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
|
+
# Jetpack Compose patterns
|
|
15
|
+
|
|
16
|
+
Write Compose UI where state lives in one place, composables skip when their inputs didn't change, and effects run exactly when intended. Kotlin language features, coroutines and Flow belong to `kotlin-patterns`; sharing code with iOS belongs to `kotlin-multiplatform`.
|
|
17
|
+
|
|
18
|
+
## 0. Before you write
|
|
19
|
+
|
|
20
|
+
1. Read the module's `build.gradle.kts` and `gradle/libs.versions.toml`: Kotlin version, Compose BOM, whether the `org.jetbrains.kotlin.plugin.compose` compiler plugin is applied (Kotlin 2.0+), and which navigation library is used (`androidx.navigation:navigation-compose` or Navigation 3).
|
|
21
|
+
2. Find the project's DI (Hilt, Koin, manual) and how ViewModels are obtained (`hiltViewModel()`, `viewModel()`, `koinViewModel()`). Follow it.
|
|
22
|
+
3. Build once (`./gradlew :app:assembleDebug` or the project's task) and run unit tests; record the result.
|
|
23
|
+
|
|
24
|
+
## 1. State hoisting
|
|
25
|
+
|
|
26
|
+
- Split each screen into a **stateful** route composable (gets the ViewModel, collects state) and a **stateless** screen composable that takes `state` and event lambdas: `ProfileScreen(state, onNameChange, onSave)`.
|
|
27
|
+
- Hoist state to the lowest common owner of everything that reads or writes it. UI-only state (scroll, expanded, focus) can stay in the composable with `remember` / `rememberSaveable`.
|
|
28
|
+
- Use `rememberSaveable` for state that must survive configuration change and process death (text input, selected tab).
|
|
29
|
+
- Every reusable composable takes `modifier: Modifier = Modifier` as its first optional parameter and applies it to its root, once.
|
|
30
|
+
- Never pass a ViewModel or `NavController` down into leaf composables; pass state and lambdas.
|
|
31
|
+
|
|
32
|
+
## 2. Recomposition and stability
|
|
33
|
+
|
|
34
|
+
- Since Kotlin 2.0.20 strong skipping is on by default: composables with unstable parameters can still skip, but unstable parameters are compared by instance (`===`), stable ones by `equals`. So a new `List` instance with the same contents still recomposes.
|
|
35
|
+
- Make UI state classes immutable `data class`es with `val`s. Use `kotlinx.collections.immutable` (`ImmutableList`) or mark classes `@Immutable` / `@Stable` only when the promise is true.
|
|
36
|
+
- Read state as late as possible: pass a lambda (`{ scrollState.value }`) or use lambda-based modifiers (`Modifier.offset { }`, `graphicsLayer { }`) for values that change every frame, so only layout or draw re-runs.
|
|
37
|
+
- Use `derivedStateOf` when a frequently changing state produces a rarely changing result (`showButton = listState.firstVisibleItemIndex > 0`). Don't use it for a simple combination of two states.
|
|
38
|
+
- `remember(key) { expensive() }` for work inside composition; never sort, filter or format large data directly in a composable body.
|
|
39
|
+
- Check with Layout Inspector recomposition counts, and the Compose compiler reports (`composeCompiler { reportsDestination = ... }`) when stability is in doubt.
|
|
40
|
+
|
|
41
|
+
## 3. Side effects
|
|
42
|
+
|
|
43
|
+
| Need | API |
|
|
44
|
+
|---|---|
|
|
45
|
+
| Run suspend work when entering composition or when a key changes | `LaunchedEffect(key)` |
|
|
46
|
+
| Register / unregister a listener or observer | `DisposableEffect(key) { ...; onDispose { } }` |
|
|
47
|
+
| Use the latest lambda inside a long-running effect without restarting it | `rememberUpdatedState(onTimeout)` |
|
|
48
|
+
| Launch coroutines from callbacks (click handlers) | `rememberCoroutineScope()` |
|
|
49
|
+
| Turn Compose state into a Flow | `snapshotFlow { state.value }` |
|
|
50
|
+
| Publish Compose state to non-Compose code after every successful composition | `SideEffect` |
|
|
51
|
+
|
|
52
|
+
Rules: keys must be the values that should restart the effect, never `Unit` when the effect reads a changing parameter. One-off events (navigate, snackbar) come from the ViewModel as state or a `Channel`/`Flow` collected in a `LaunchedEffect`, never by calling navigation inside composition.
|
|
53
|
+
|
|
54
|
+
## 4. ViewModel and lifecycle
|
|
55
|
+
|
|
56
|
+
- The ViewModel exposes one `StateFlow<UiState>` built with `stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), initial)` or a `MutableStateFlow` it updates.
|
|
57
|
+
- Collect it with `collectAsStateWithLifecycle()` (from `androidx.lifecycle:lifecycle-runtime-compose`), not `collectAsState()`, so collection stops when the app is in the background.
|
|
58
|
+
- Business logic, network and database work live in the ViewModel and repositories, never in composables.
|
|
59
|
+
- Use `SavedStateHandle` for navigation arguments and small state that must survive process death.
|
|
60
|
+
|
|
61
|
+
## 5. Navigation
|
|
62
|
+
|
|
63
|
+
- Navigation Compose 2.8+: define routes as `@Serializable` objects and data classes, `composable<Profile> { entry -> entry.toRoute<Profile>() }`, and navigate with `navController.navigate(Profile(id))`. Don't build string routes by hand in new code.
|
|
64
|
+
- Navigation 3 (stable since late 2025) keeps the back stack as your own state. Use it only if the project already does or the user chooses it.
|
|
65
|
+
- Pass IDs, not objects. Load data in the destination's ViewModel via `SavedStateHandle`.
|
|
66
|
+
- Keep the `NavController` / back stack at the top-level `NavHost`; leaf screens receive `onNavigateToX` lambdas.
|
|
67
|
+
- Handle system back with `BackHandler` only where you must intercept it (unsaved changes).
|
|
68
|
+
|
|
69
|
+
## 6. Performance
|
|
70
|
+
|
|
71
|
+
- Measure in a **release** build (R8 on, not debuggable); debug Compose is much slower.
|
|
72
|
+
- `LazyColumn` / `LazyRow`: give items a stable `key = { it.id }` and a `contentType` when rows differ. Never nest a vertically scrolling lazy list inside a `Column` with `verticalScroll`.
|
|
73
|
+
- Avoid allocating in item content: hoist `Modifier` chains and formatters, `remember` them.
|
|
74
|
+
- Add a Baseline Profile (`androidx.baselineprofile` plugin and a Macrobenchmark module) for startup and scroll paths in production apps.
|
|
75
|
+
- Use Macrobenchmark `FrameTimingMetric` to prove a jank fix.
|
|
76
|
+
|
|
77
|
+
**Stop and ask the user** before switching navigation libraries, raising `minSdk`, or upgrading Kotlin or the Compose BOM as part of an unrelated change.
|
|
78
|
+
|
|
79
|
+
## Never
|
|
80
|
+
|
|
81
|
+
- Never launch coroutines, navigate or write to state directly in a composable body.
|
|
82
|
+
- Never use `collectAsState()` for ViewModel state on Android when lifecycle-aware collection is available.
|
|
83
|
+
- Never annotate a class `@Stable` or `@Immutable` that has mutable public state.
|
|
84
|
+
- Never use `Unit` as an effect key when the effect depends on a parameter.
|
|
85
|
+
|
|
86
|
+
## Report back
|
|
87
|
+
|
|
88
|
+
End with: composables and ViewModels changed, where each new piece of state lives, Gradle tasks run with results, and whether you checked recomposition or frame timing (and how).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"My Compose LazyColumn recomposes every row when one item is liked",
|
|
4
|
+
"Should I use LaunchedEffect or DisposableEffect to register this location listener in a composable?",
|
|
5
|
+
"Hoist the state out of this Compose text field so the ViewModel owns it",
|
|
6
|
+
"Switch our NavHost to type-safe @Serializable routes",
|
|
7
|
+
"collectAsState vs collectAsStateWithLifecycle for my ViewModel StateFlow"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Use Compose Multiplatform so the same UI runs on iOS and Android",
|
|
11
|
+
"Write a coroutine Flow that debounces search queries in plain Kotlin",
|
|
12
|
+
"Fix this Gradle build failure: Unresolved reference in the Kotlin module",
|
|
13
|
+
"Build a SwiftUI list with lazy loading of images",
|
|
14
|
+
"Review this Kotlin pull request for unsafe !! and leaked coroutine scopes"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jira-integration
|
|
3
|
+
description: Connects code to Jira Cloud through the REST API v3, from picking basic auth with an Atlassian API token or OAuth 2.0 (3LO) with a cloudId, through enhanced JQL search on /rest/api/3/search/jql with nextPageToken paging, creating issues with Atlassian Document Format bodies, looking up custom field IDs and running workflow transitions, to registering webhooks and coping with 429 burst and points-based quotas. Use when building a Jira sync, bot or importer, moving off the removed /rest/api/3/search endpoint, setting custom fields or transitions from code, or handling Jira webhooks and rate limits.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Live calls need network access to <site>.atlassian.net or api.atlassian.com. Covers Jira Cloud only; Jira Data Center and Server have a different API surface and auth.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: integrations
|
|
9
|
+
kete-loom/risk: network
|
|
10
|
+
kete-loom/requires: "curl"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Jira Cloud integration
|
|
15
|
+
|
|
16
|
+
Build a Jira integration that authenticates with the right credential, finds issues with paged JQL, writes fields Jira accepts, moves issues through the real workflow, and slows down when Jira says so.
|
|
17
|
+
|
|
18
|
+
## Ground rules
|
|
19
|
+
|
|
20
|
+
1. **Jira Cloud, REST v3.** Base URL `https://<site>.atlassian.net/rest/api/3/` for API-token calls, or `https://api.atlassian.com/ex/jira/{cloudId}/rest/api/3/` for OAuth 2.0 and scoped tokens. If the user is on Data Center or Server, stop: this skill does not cover it.
|
|
21
|
+
2. **Credentials from the environment.** Never commit an API token or client secret, never log the `Authorization` header.
|
|
22
|
+
3. **Never hard-code IDs** for custom fields, issue types or transitions. They differ per site and per project; look them up and cache them.
|
|
23
|
+
4. **People are account IDs.** Set assignee and reporter with `accountId`; usernames and email lookups are not reliable in Cloud.
|
|
24
|
+
5. **Text fields in v3 are ADF** (Atlassian Document Format) JSON, not plain strings or wiki markup.
|
|
25
|
+
|
|
26
|
+
## Choose the auth
|
|
27
|
+
|
|
28
|
+
| Situation | Use |
|
|
29
|
+
|---|---|
|
|
30
|
+
| Script or internal service for one site | Basic auth: `email:api_token` base64-encoded in `Authorization: Basic ...`. Tokens are created at id.atlassian.com, expire (1 to 365 days) and act as that user |
|
|
31
|
+
| Narrower internal access | A scoped API token, called through `api.atlassian.com/ex/jira/{cloudId}` |
|
|
32
|
+
| App other people's sites install, acting as each user | OAuth 2.0 (3LO) |
|
|
33
|
+
| Code running inside Jira (UI, triggers) | Forge app; out of scope here |
|
|
34
|
+
|
|
35
|
+
Use a dedicated service account for shared integrations, not a person's token. Stop and ask the user before using an admin's token.
|
|
36
|
+
|
|
37
|
+
### OAuth 2.0 (3LO)
|
|
38
|
+
|
|
39
|
+
1. Register the app in the Atlassian developer console; add the Jira API scopes you need (classic `read:jira-work`, `write:jira-work`, `read:jira-user`, or granular scopes) plus `offline_access` for refresh tokens.
|
|
40
|
+
2. Send the user to `https://auth.atlassian.com/authorize?audience=api.atlassian.com&client_id=...&scope=...&redirect_uri=...&state=...&response_type=code&prompt=consent`. Check `state` on return.
|
|
41
|
+
3. Exchange the code at `POST https://auth.atlassian.com/oauth/token`. Refresh tokens rotate: store the new refresh token every time you refresh.
|
|
42
|
+
4. `GET https://api.atlassian.com/oauth/token/accessible-resources` lists the sites the token can reach; store each `id` as the `cloudId` and call `api.atlassian.com/ex/jira/{cloudId}/...`.
|
|
43
|
+
|
|
44
|
+
## Search with JQL
|
|
45
|
+
|
|
46
|
+
- `GET/POST /rest/api/3/search` was removed from Jira Cloud. Use `POST /rest/api/3/search/jql` with `{ "jql": "...", "fields": [...], "maxResults": 100, "nextPageToken": "..." }`.
|
|
47
|
+
- Always pass `fields` explicitly; ask for only what you use.
|
|
48
|
+
- Page by sending back `nextPageToken` until the response has none (or `isLast` is true). There is no `startAt` and no `total`; for a count, use `POST /rest/api/3/search/approximate-count`.
|
|
49
|
+
- Always bound JQL (`project = ABC AND updated >= -1d ORDER BY updated ASC`). An unbounded query on a large site is slow and burns quota.
|
|
50
|
+
- Quote values with spaces or reserved words, and build JQL from validated values, never by pasting user input into the string.
|
|
51
|
+
- For incremental sync, store the last `updated` you processed and query `updated >= "<timestamp>"`, then dedupe by issue ID (JQL date precision is minutes).
|
|
52
|
+
|
|
53
|
+
## Create and update issues
|
|
54
|
+
|
|
55
|
+
1. Discover fields for the target: `GET /rest/api/3/issue/createmeta/{projectIdOrKey}/issuetypes` then `.../issuetypes/{issueTypeId}` for required fields and allowed values. `GET /rest/api/3/field` maps custom field names to IDs (`customfield_10042`); cache by name, look up once per site.
|
|
56
|
+
2. `POST /rest/api/3/issue` with `fields`: `project: {key}`, `issuetype: {id}`, `summary`, `description` as ADF, plus required custom fields in their type's shape (select: `{ "value": "High" }` or `{ "id": "..." }`; user: `{ "accountId": "..." }`; number and date as plain values).
|
|
57
|
+
3. Minimal ADF: `{"type":"doc","version":1,"content":[{"type":"paragraph","content":[{"type":"text","text":"..."}]}]}`.
|
|
58
|
+
4. Update with `PUT /rest/api/3/issue/{key}` using `fields` (set) or `update` (add/remove on multi-value fields such as labels). Add `notifyUsers=false` only if the user asks; it needs the Administer Jira or Administer Projects permission.
|
|
59
|
+
5. Comments: `POST /rest/api/3/issue/{key}/comment` with an ADF `body`.
|
|
60
|
+
6. Make creates idempotent yourself: Jira has no idempotency key. Store your external ID in an issue property or a label and search for it before creating.
|
|
61
|
+
7. Read the 400 response `errors` object; it names each field that failed.
|
|
62
|
+
|
|
63
|
+
## Transitions
|
|
64
|
+
|
|
65
|
+
1. `GET /rest/api/3/issue/{key}/transitions` lists transitions available **now** for that issue and user (add `expand=transitions.fields` to see screen fields).
|
|
66
|
+
2. Pick by target status (`to.name` or `to.id`), not by transition name or a hard-coded ID; workflows differ per project.
|
|
67
|
+
3. `POST /rest/api/3/issue/{key}/transitions` with `{ "transition": { "id": "31" }, "fields": { "resolution": { "name": "Done" } } }` when the screen requires fields.
|
|
68
|
+
4. If the target is not listed, the workflow does not allow it from the current status or the user lacks permission. Report that; do not try other transitions to get there unless the user asks.
|
|
69
|
+
|
|
70
|
+
## Webhooks
|
|
71
|
+
|
|
72
|
+
- Admin-created webhooks (Jira settings or `/rest/webhooks/1.0/webhook`): set a `secret`; Jira then sends `X-Hub-Signature: sha256=<hex HMAC of the raw body>`. Verify on the raw bytes with a constant-time compare.
|
|
73
|
+
- OAuth 2.0 and Connect apps register dynamic webhooks with `POST /rest/api/3/webhook` (JQL filter limited to fields such as `project`, `issuetype`, `status`, `assignee`). They expire after 30 days: refresh with `PUT /rest/api/3/webhook/refresh` and `{ "webhookIds": [...] }` on a schedule. An OAuth app can register 5 per user per site. OAuth apps receive a JWT in `Authorization` signed with the client secret; verify it.
|
|
74
|
+
- Jira retries failed deliveries (408, 409, 425, 429, 5xx, timeouts) up to five times. Dedupe on `X-Atlassian-Webhook-Identifier`; `X-Atlassian-Webhook-Retry` gives the attempt number.
|
|
75
|
+
- Acknowledge fast and process from a queue. The payload may be stale; re-fetch the issue if you need current state.
|
|
76
|
+
|
|
77
|
+
## Rate limits
|
|
78
|
+
|
|
79
|
+
- Jira returns `429` with `Retry-After` (seconds) and `RateLimit-Reason` (`jira-burst-based`, `jira-quota-global-based`, `jira-quota-tenant-based`, `jira-per-issue-on-write`). Watch `X-RateLimit-NearLimit: true`.
|
|
80
|
+
- Limits: per-second burst limits per endpoint, an hourly points quota for apps (enforced from March 2026; API-token traffic stays under burst limits), and per-issue write limits (20 writes per 2 seconds, 100 per 30 seconds on one issue).
|
|
81
|
+
- Wait at least `Retry-After`, then exponential backoff with jitter, capped at a few retries; retry only idempotent requests. On quota reasons pause all calls; on burst slow that endpoint; on per-issue space out writes to that issue.
|
|
82
|
+
- Batch where Jira offers it (bulk create `POST /rest/api/3/issue/bulk`, up to 50 issues) and fetch only needed fields.
|
|
83
|
+
|
|
84
|
+
## Stop and ask the user
|
|
85
|
+
|
|
86
|
+
- Before bulk edits, bulk transitions or deletes on more than a handful of issues; show the JQL and the count first.
|
|
87
|
+
- Before creating webhooks, custom fields or workflow changes on a production site.
|
|
88
|
+
- If required fields or the workflow make the requested change impossible, rather than guessing values.
|
|
89
|
+
|
|
90
|
+
## Before you finish
|
|
91
|
+
|
|
92
|
+
Test against a sandbox site or a throwaway project: a search that spans more than one page, a create with every required custom field, a transition that needs a screen field, a duplicate create (no second issue), a webhook with a bad signature (rejected), and a forced 429 handled with backoff. Atlassian's REST v3 reference at developer.atlassian.com is the final word on endpoints and payloads.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Our Jira sync broke because /rest/api/3/search now returns 410, move it to the new JQL search endpoint",
|
|
4
|
+
"Create Jira issues from our support tool and fill the Severity custom field from code",
|
|
5
|
+
"Move a Jira ticket to Done through the REST API when the related PR merges",
|
|
6
|
+
"Set up OAuth 2.0 3LO so customers can connect their Jira Cloud site to our app",
|
|
7
|
+
"The Jira importer keeps getting 429 with RateLimit-Reason jira-burst-based, add proper backoff",
|
|
8
|
+
"Verify the X-Hub-Signature on webhooks coming from our Jira Cloud admin webhook"
|
|
9
|
+
],
|
|
10
|
+
"should_not_trigger": [
|
|
11
|
+
"Create Linear issues from our error tracker using the GraphQL API",
|
|
12
|
+
"Write a JQL filter in the Jira UI that shows my team's overdue bugs",
|
|
13
|
+
"Estimate how many sprints the checkout rewrite will take",
|
|
14
|
+
"Open a GitHub issue automatically when the nightly job fails",
|
|
15
|
+
"Write the incident postmortem for last night's outage"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jpa-hibernate
|
|
3
|
+
description: Jakarta Persistence and Hibernate ORM data-layer rules for entity and association mapping, LAZY-by-default fetch plans, finding and fixing N+1 selects with JOIN FETCH, entity graphs or batch fetching, transaction scope and LazyInitializationException, @Version optimistic locking, and evolving the schema through Flyway or Liquibase rather than ddl-auto. Use when mapping JPA entities, chasing slow or repeated SQL from Hibernate, fixing lazy loading or stale-update errors, or adding a database migration in a Java or Kotlin JVM project.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Needs a JDK and the project's build wrapper. Written for Jakarta Persistence 3.x with Hibernate ORM 6.x; applies under Spring Boot, Quarkus or plain Jakarta EE.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: data
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/requires: "java"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# JPA and Hibernate
|
|
15
|
+
|
|
16
|
+
Hibernate hides SQL, which is why most of its bugs are invisible until production. Make the SQL visible, load exactly what each use case needs, and let migrations own the schema. Mapping examples are in `references/mapping.md`.
|
|
17
|
+
|
|
18
|
+
## 0. Before you start
|
|
19
|
+
|
|
20
|
+
1. Find the Hibernate version (Hibernate 6 uses `jakarta.persistence`; 5 and older use `javax.persistence`) and the framework (Spring Data JPA, Quarkus Panache, plain `EntityManager`).
|
|
21
|
+
2. Find the migration tool: `db/migration/V*__*.sql` (Flyway) or a `db.changelog-master` file (Liquibase). If neither exists and the schema comes from `ddl-auto`, tell the user and propose adding one before changing tables.
|
|
22
|
+
3. Turn on SQL logging in dev or tests so you can count statements: `spring.jpa.show-sql=true` / `quarkus.hibernate-orm.log.sql=true`, or log `org.hibernate.SQL` at DEBUG. Add `hibernate.generate_statistics=true` when chasing N+1.
|
|
23
|
+
|
|
24
|
+
## 1. Entity mapping rules
|
|
25
|
+
|
|
26
|
+
- Every entity has a surrogate id. Use `GenerationType.SEQUENCE` (or `IDENTITY` on MySQL). `IDENTITY` disables JDBC insert batching; prefer sequences where the database supports them.
|
|
27
|
+
- `equals`/`hashCode`: base them on a natural business key, or on the id with a constant `hashCode()` (`getClass().hashCode()`). Never include associations or lazy fields. In Kotlin, never use a `data class` for an entity.
|
|
28
|
+
- Map enums with `@Enumerated(EnumType.STRING)`, never the default `ORDINAL`.
|
|
29
|
+
- Money: `BigDecimal` with explicit `precision` and `scale`, or integer minor units. Never `double`.
|
|
30
|
+
- Time: `Instant` or `OffsetDateTime` for points in time; store in UTC (`hibernate.jdbc.time_zone=UTC`; in Spring Boot `spring.jpa.properties.hibernate.jdbc.time_zone=UTC`).
|
|
31
|
+
- Entities need a no-arg constructor (protected is fine) and must not be `final` if they are proxied lazily (Kotlin: use the `kotlin-jpa` and `allopen` compiler plugins).
|
|
32
|
+
|
|
33
|
+
## 2. Associations and fetch strategy
|
|
34
|
+
|
|
35
|
+
- **Make every association LAZY.** `@ManyToOne` and `@OneToOne` are EAGER by default: always write `fetch = FetchType.LAZY`. Collections are already lazy.
|
|
36
|
+
- Prefer unidirectional `@ManyToOne` from child to parent. Add the `@OneToMany(mappedBy = ...)` side only when you really navigate it, and keep both sides in sync with helper methods (`addItem`, `removeItem`).
|
|
37
|
+
- Avoid `@ManyToMany` when the join has attributes or might gain them; model the join table as its own entity.
|
|
38
|
+
- Use `Set` or `List` deliberately: fetching two `List` collections with `JOIN FETCH` in one query throws `MultipleBagFetchException`. Fetch one collection per query, or use `Set`.
|
|
39
|
+
- `cascade = ALL` and `orphanRemoval = true` belong only on true parent-owned children (order → order lines), never on references to shared entities (order → customer).
|
|
40
|
+
|
|
41
|
+
## 3. Finding and fixing N+1
|
|
42
|
+
|
|
43
|
+
Symptom: one query for the list, then one more per row. Confirm by counting statements in the SQL log or statistics before changing anything.
|
|
44
|
+
|
|
45
|
+
Fix with the narrowest tool:
|
|
46
|
+
|
|
47
|
+
1. **Fetch join** for a use case that always needs the association: `select o from Order o join fetch o.customer where o.status = :s`.
|
|
48
|
+
2. **Entity graph** to choose fetches per query without rewriting JPQL: `@EntityGraph(attributePaths = {"customer", "lines"})` on a Spring Data method, or a `jakarta.persistence.fetchgraph` hint.
|
|
49
|
+
3. **Batch fetching** to turn N queries into N/size: `@BatchSize(size = 50)` on the association or `hibernate.default_batch_fetch_size=50` globally. Good default for collections you can't fetch-join.
|
|
50
|
+
4. **DTO projection** for read-only screens: `select new com.acme.OrderRow(o.id, c.name) from Order o join o.customer c`, or a Spring Data interface/record projection. No entities, no dirty checking, no lazy surprises.
|
|
51
|
+
|
|
52
|
+
Don't fix N+1 by switching to `FetchType.EAGER`; that loads the association everywhere and often causes more queries.
|
|
53
|
+
|
|
54
|
+
Paging with collection fetch joins makes Hibernate load every row and page in memory (it logs a warning about applying the limit in memory). Page the ids first, then fetch the entities with their collections by id. Set `hibernate.query.fail_on_pagination_over_collection_fetch=true` so this fails in tests instead of silently loading everything.
|
|
55
|
+
|
|
56
|
+
## 4. Transactions and lazy loading
|
|
57
|
+
|
|
58
|
+
- Load and use entities inside one transaction, at the service layer. `LazyInitializationException` means a lazy field was touched after the session closed; fix it by fetching what the use case needs (section 3) or mapping to a DTO inside the transaction.
|
|
59
|
+
- **Do not** fix it with `hibernate.enable_lazy_load_no_trans` or by enabling open-session-in-view; both hide extra queries and run them outside a transaction. In Spring Boot set `spring.jpa.open-in-view=false`.
|
|
60
|
+
- Changes to managed entities are flushed automatically at commit (dirty checking). Calling `save()` on an already managed entity is unnecessary.
|
|
61
|
+
- Use read-only transactions for queries (`@Transactional(readOnly = true)` in Spring) so Hibernate skips dirty checking.
|
|
62
|
+
- Bulk `update`/`delete` JPQL bypasses the persistence context; clear it afterwards (`@Modifying(clearAutomatically = true)` in Spring Data) or reload entities.
|
|
63
|
+
- For large batch jobs: set `hibernate.jdbc.batch_size`, and `flush()` plus `clear()` every N entities so memory stays flat.
|
|
64
|
+
|
|
65
|
+
## 5. Optimistic locking
|
|
66
|
+
|
|
67
|
+
- Add `@Version private long version;` (or `Long`/`Integer`) to entities edited concurrently by users or jobs. Hibernate adds `where version = ?` to updates and throws `OptimisticLockException` (Spring: `ObjectOptimisticLockingFailureException`) when another transaction got there first.
|
|
68
|
+
- Expose the version to clients (an `ETag` header or a field in the DTO) and send it back on update, so a stale form fails instead of overwriting someone's change. Map the exception to HTTP 409 or 412.
|
|
69
|
+
- Retry only when the operation is safe to repeat (for example a counter increment), reloading the entity each time, with a small retry limit.
|
|
70
|
+
- Use pessimistic locks (`LockModeType.PESSIMISTIC_WRITE`, `select ... for update`) only for short critical sections such as allocating stock, and always inside a transaction with a lock timeout.
|
|
71
|
+
- Never set the version field by hand.
|
|
72
|
+
|
|
73
|
+
## 6. Migrations (Flyway or Liquibase)
|
|
74
|
+
|
|
75
|
+
- The schema is owned by migrations. Set schema generation (`spring.jpa.hibernate.ddl-auto`; Quarkus `quarkus.hibernate-orm.schema-management.strategy`, called `database.generation` before 3.23) to `validate` or `none` everywhere except throwaway local experiments.
|
|
76
|
+
- Flyway: versioned files `V<version>__<description>.sql` under `db/migration`, applied in order and checksummed. **Never edit a migration that has run anywhere shared**; add a new one. Repeatable `R__` scripts are for views and functions.
|
|
77
|
+
- Liquibase: changesets identified by `id` + `author` + file; same rule, never change an applied changeset.
|
|
78
|
+
- Write backward-compatible migrations so old and new app versions can run during a rolling deploy (expand, migrate data, then contract in a later release). Example: add a nullable column, backfill, then add `NOT NULL` later.
|
|
79
|
+
- Add indexes for every foreign key and for columns in frequent `where`/`order by`. On large PostgreSQL tables use `create index concurrently` (in Flyway, put it in its own migration so it runs outside a transaction; Flyway cannot mix it with transactional statements; if the migration hangs on Flyway's lock, set `flyway.postgresql.transactional.lock=false`).
|
|
80
|
+
- **Stop and ask the user** before any migration that drops or renames columns or tables, rewrites a large table, or deletes data.
|
|
81
|
+
- Test migrations against the real database engine (Testcontainers), not H2, and run `validate` in CI.
|
|
82
|
+
|
|
83
|
+
## Never
|
|
84
|
+
|
|
85
|
+
- EAGER fetching to silence a lazy loading error, or `enable_lazy_load_no_trans`.
|
|
86
|
+
- `ddl-auto=update` in production, or editing an applied migration.
|
|
87
|
+
- Entities serialised directly to JSON.
|
|
88
|
+
- `ORDINAL` enums, `double` money, or `equals` over lazy associations.
|
|
89
|
+
|
|
90
|
+
## Before you finish
|
|
91
|
+
|
|
92
|
+
Run the test suite with SQL logging on for the changed queries. Report: entities and mappings changed, the statement count before and after for any N+1 fix, migrations added (file names), whether they are backward compatible, and the final test counts.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Our Hibernate entities trigger hundreds of selects when loading the order list, fix the N+1",
|
|
4
|
+
"Getting LazyInitializationException: could not initialize proxy - no Session when mapping to DTO",
|
|
5
|
+
"Add @Version optimistic locking to the JPA Account entity and return 409 on conflicts",
|
|
6
|
+
"How should I map a one-to-many between Order and OrderLine in JPA with cascade and orphan removal?",
|
|
7
|
+
"Replace spring.jpa.hibernate.ddl-auto=update with Flyway migrations for our entities",
|
|
8
|
+
"MultipleBagFetchException when I join fetch two lists in a JPQL query"
|
|
9
|
+
],
|
|
10
|
+
"should_not_trigger": [
|
|
11
|
+
"Add a new REST endpoint to our Spring Boot order service with request validation and proper 404 responses",
|
|
12
|
+
"Tune this slow PostgreSQL query plan and suggest indexes",
|
|
13
|
+
"Set up Prisma schema and migrations for our Node backend",
|
|
14
|
+
"Configure EF Core migrations and fix the tracking issue in our ASP.NET Core app",
|
|
15
|
+
"Plan a zero-downtime schema migration strategy for our Rails app",
|
|
16
|
+
"Write an Exposed DSL query in our Ktor service to list users"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# JPA mapping examples
|
|
2
|
+
|
|
3
|
+
Jakarta Persistence 3.x / Hibernate 6. Adjust imports to `javax.persistence` on Hibernate 5.
|
|
4
|
+
|
|
5
|
+
## Parent with owned children
|
|
6
|
+
|
|
7
|
+
```java
|
|
8
|
+
@Entity
|
|
9
|
+
@Table(name = "orders")
|
|
10
|
+
public class Order {
|
|
11
|
+
@Id
|
|
12
|
+
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "orders_seq")
|
|
13
|
+
@SequenceGenerator(name = "orders_seq", sequenceName = "orders_seq", allocationSize = 50)
|
|
14
|
+
private Long id;
|
|
15
|
+
|
|
16
|
+
@Version
|
|
17
|
+
private long version;
|
|
18
|
+
|
|
19
|
+
@Enumerated(EnumType.STRING)
|
|
20
|
+
@Column(nullable = false, length = 20)
|
|
21
|
+
private OrderStatus status;
|
|
22
|
+
|
|
23
|
+
@Column(nullable = false, precision = 19, scale = 2)
|
|
24
|
+
private BigDecimal total;
|
|
25
|
+
|
|
26
|
+
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
|
27
|
+
@JoinColumn(name = "customer_id", nullable = false)
|
|
28
|
+
private Customer customer;
|
|
29
|
+
|
|
30
|
+
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
|
|
31
|
+
private Set<OrderLine> lines = new HashSet<>();
|
|
32
|
+
|
|
33
|
+
protected Order() {}
|
|
34
|
+
|
|
35
|
+
public void addLine(OrderLine line) { lines.add(line); line.setOrder(this); }
|
|
36
|
+
public void removeLine(OrderLine line) { lines.remove(line); line.setOrder(null); }
|
|
37
|
+
|
|
38
|
+
@Override public boolean equals(Object o) {
|
|
39
|
+
if (this == o) return true;
|
|
40
|
+
if (!(o instanceof Order other)) return false;
|
|
41
|
+
return id != null && id.equals(other.id);
|
|
42
|
+
}
|
|
43
|
+
@Override public int hashCode() { return getClass().hashCode(); }
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`allocationSize` must match the sequence's `INCREMENT BY` in the migration, or ids collide or Hibernate fails validation.
|
|
48
|
+
|
|
49
|
+
## Matching Flyway migration
|
|
50
|
+
|
|
51
|
+
```sql
|
|
52
|
+
-- V3__create_orders.sql
|
|
53
|
+
create sequence orders_seq start with 1 increment by 50;
|
|
54
|
+
|
|
55
|
+
create table orders (
|
|
56
|
+
id bigint primary key,
|
|
57
|
+
version bigint not null default 0,
|
|
58
|
+
status varchar(20) not null,
|
|
59
|
+
total numeric(19, 2) not null,
|
|
60
|
+
customer_id bigint not null references customers (id)
|
|
61
|
+
);
|
|
62
|
+
create index idx_orders_customer_id on orders (customer_id);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Spring Data: entity graph and projection
|
|
66
|
+
|
|
67
|
+
```java
|
|
68
|
+
public interface OrderRepository extends JpaRepository<Order, Long> {
|
|
69
|
+
|
|
70
|
+
@EntityGraph(attributePaths = {"customer", "lines"})
|
|
71
|
+
Optional<Order> findWithLinesById(Long id);
|
|
72
|
+
|
|
73
|
+
@Query("select new com.acme.orders.OrderRow(o.id, c.name, o.total) " +
|
|
74
|
+
"from Order o join o.customer c where o.status = :status")
|
|
75
|
+
Page<OrderRow> findRows(@Param("status") OrderStatus status, Pageable pageable);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Paging a collection fetch without in-memory paging
|
|
80
|
+
|
|
81
|
+
```java
|
|
82
|
+
List<Long> ids = em.createQuery(
|
|
83
|
+
"select o.id from Order o where o.status = :status order by o.id", Long.class)
|
|
84
|
+
.setParameter("status", status)
|
|
85
|
+
.setFirstResult(page * size)
|
|
86
|
+
.setMaxResults(size)
|
|
87
|
+
.getResultList();
|
|
88
|
+
|
|
89
|
+
List<Order> orders = em.createQuery(
|
|
90
|
+
"select distinct o from Order o join fetch o.lines where o.id in :ids order by o.id", Order.class)
|
|
91
|
+
.setParameter("ids", ids)
|
|
92
|
+
.getResultList();
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Kotlin notes
|
|
96
|
+
|
|
97
|
+
- Apply the `kotlin("plugin.jpa")` (no-arg) and `kotlin("plugin.allopen")` plugins, with `allOpen { annotation("jakarta.persistence.Entity") }` so lazy proxies work.
|
|
98
|
+
- Use a regular `class` with `var` properties, not a `data class`.
|
|
99
|
+
- Collections: `val lines: MutableSet<OrderLine> = mutableSetOf()`.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: kotlin-multiplatform
|
|
3
|
+
description: Sets up and evolves Kotlin Multiplatform (KMP) and Compose Multiplatform projects, covering the shared module and source-set layout (commonMain, androidMain, iosMain), expect/actual versus interfaces with injected platform implementations, choosing multiplatform-ready libraries, exporting a framework to Xcode, exposing suspend functions and Flows to Swift, and Gradle build setup for Android and iOS targets. Use when starting or restructuring a KMP shared module, deciding what code to share between Android and iOS, adding a platform-specific API behind expect/actual, wiring the shared framework into an Xcode project, or sharing UI with Compose Multiplatform.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Needs a JDK and the Gradle wrapper; building iOS targets needs macOS with Xcode.
|
|
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
|
+
# Kotlin Multiplatform
|
|
15
|
+
|
|
16
|
+
Share the code that is the same on every platform, keep platform code behind a small seam, and keep the iOS side pleasant to call from Swift. Single-platform Kotlin idioms belong to `kotlin-patterns`; Android-only Compose UI belongs to `jetpack-compose-patterns`; native iOS UI belongs to `swiftui-patterns`.
|
|
17
|
+
|
|
18
|
+
## 0. Before you change anything
|
|
19
|
+
|
|
20
|
+
1. Read `settings.gradle.kts`, the shared module's `build.gradle.kts` and `gradle/libs.versions.toml`: Kotlin version, targets declared in `kotlin { }`, Android plugin in use, and whether Compose Multiplatform (`org.jetbrains.compose`) is applied.
|
|
21
|
+
2. Find how iOS consumes the shared code: a framework built by an Xcode run-script phase (`embedAndSignAppleFrameworkForXcode`), CocoaPods (`kotlin("native.cocoapods")`), or Swift Package Manager via an XCFramework.
|
|
22
|
+
3. Run `./gradlew build` (or `:shared:allTests`) once and record the result. On a machine without Xcode, iOS tasks will be skipped or fail; say so in your report.
|
|
23
|
+
|
|
24
|
+
## 1. What to share
|
|
25
|
+
|
|
26
|
+
Decision rule:
|
|
27
|
+
|
|
28
|
+
1. **Share**: domain models, business rules, validation, networking (Ktor client), persistence (SQLDelight, Room KMP), serialization (`kotlinx.serialization`), repositories and ViewModel-level presentation logic.
|
|
29
|
+
2. **Share UI with Compose Multiplatform** only when the team accepts a non-native-looking iOS UI or has designed for it. Compose Multiplatform for iOS has been stable since 1.8.0.
|
|
30
|
+
3. **Keep native**: platform UI the user expects (iOS system navigation, widgets, extensions), and anything that needs deep platform APIs.
|
|
31
|
+
|
|
32
|
+
**Stop and ask the user** before moving existing native UI into Compose Multiplatform or moving a large existing module into shared code.
|
|
33
|
+
|
|
34
|
+
## 2. Module and source-set structure
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
shared/
|
|
38
|
+
src/commonMain/kotlin/ # pure Kotlin, multiplatform libraries only
|
|
39
|
+
src/commonTest/kotlin/ # kotlin.test tests that run on every target
|
|
40
|
+
src/androidMain/kotlin/ # Android actuals, Android SDK allowed
|
|
41
|
+
src/iosMain/kotlin/ # shared by iosArm64 + iosSimulatorArm64 (+ iosX64)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- Rely on the default hierarchy template: declaring `iosArm64()` and `iosSimulatorArm64()` gives you an `iosMain` source set automatically. Don't hand-wire `dependsOn` unless the project already does.
|
|
45
|
+
- `commonMain` must never import `android.*`, `java.*` or `platform.*`. If it needs one, that's a seam (section 3).
|
|
46
|
+
- For larger apps, split by feature (`:shared:feature-auth`, `:shared:core-network`) and export only an umbrella module to iOS, because each exported framework carries its own copy of the Kotlin runtime.
|
|
47
|
+
- Android: new KMP library modules use the Android-KMP library plugin (`com.android.kotlin.multiplatform.library`). Configure the Android target inside `kotlin { }` with `android { namespace = ...; compileSdk = ...; minSdk = ... }` on AGP 8.12+, or `androidLibrary { }` on older AGP (`androidLibrary` is deprecated in newer AGP). Applying `com.android.library` to a KMP module relies on AGP APIs that need an opt-in from AGP 9.0 and are planned for removal in AGP 10; migrate only when the user agrees.
|
|
48
|
+
|
|
49
|
+
## 3. expect/actual vs interfaces
|
|
50
|
+
|
|
51
|
+
Prefer, in this order:
|
|
52
|
+
|
|
53
|
+
1. **A multiplatform library** that already wraps the API (Ktor, `kotlinx-datetime`, `multiplatform-settings`, Okio).
|
|
54
|
+
2. **An interface in `commonMain`** implemented per platform and injected (Koin or constructor injection). Best for anything stateful or that needs mocking in tests; iOS implementations can even be written in Swift and passed in.
|
|
55
|
+
3. **`expect fun` / `expect val`** for small, stateless platform facts (`expect fun platformName(): String`, a UUID or clock).
|
|
56
|
+
4. **`expect class`** last. expect/actual classes are still Beta and need the compiler opt-in the build reports when you use them.
|
|
57
|
+
|
|
58
|
+
Every `expect` needs an `actual` in each target's source set (or an intermediate set like `iosMain`). Keep actuals thin: translate and delegate, no business logic.
|
|
59
|
+
|
|
60
|
+
## 4. Platform dependencies
|
|
61
|
+
|
|
62
|
+
- Put each dependency in the narrowest source set: `commonMain.dependencies { }` for multiplatform libraries, `androidMain.dependencies { }` / `iosMain.dependencies { }` for platform engines (for example `ktor-client-okhttp` vs `ktor-client-darwin`).
|
|
63
|
+
- Before adding a library to `commonMain`, check its published targets include every target you declare (look for iOS artifacts on Maven Central or klibs.io).
|
|
64
|
+
- Use the version catalog; don't hard-code versions in build scripts.
|
|
65
|
+
- Calling Apple frameworks from Kotlin goes through the `platform.*` cinterop bindings in `iosMain`; CocoaPods or custom cinterop are only for third-party native libraries.
|
|
66
|
+
|
|
67
|
+
## 5. iOS interop
|
|
68
|
+
|
|
69
|
+
- Configure the framework once:
|
|
70
|
+
|
|
71
|
+
```kotlin
|
|
72
|
+
listOf(iosArm64(), iosSimulatorArm64()).forEach {
|
|
73
|
+
it.binaries.framework { baseName = "Shared"; isStatic = true }
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
- Kotlin `suspend` functions appear in Swift as async functions or completion handlers, and Flows arrive as plain objects that are awkward to collect. For a Swift-friendly API, use SKIE or KMP-NativeCoroutines if the project allows a plugin; otherwise expose a small callback wrapper (`fun watch(onEach: (T) -> Unit): Closeable`).
|
|
77
|
+
- Sealed classes and enums lose exhaustiveness in Swift without such tooling. Keep the Swift-facing API small and explicit: one facade per feature.
|
|
78
|
+
- Kotlin exceptions crossing into Swift crash unless declared with `@Throws`. Annotate every public suspend or throwing function exported to iOS, or return a result type.
|
|
79
|
+
- Keep all shared code that touches UI-observable state on the main dispatcher when called from Swift.
|
|
80
|
+
- After a shared-code change, rebuild the iOS app from Xcode (or `xcodebuild`) to prove the framework links.
|
|
81
|
+
|
|
82
|
+
## 6. Build setup and checks
|
|
83
|
+
|
|
84
|
+
- Use the same Kotlin version for the Kotlin, Compose compiler and serialization plugins; mismatches are the most common build failure.
|
|
85
|
+
- Common tests go in `commonTest` with `kotlin.test`; run `./gradlew :shared:allTests` (or the project's task) so they run on JVM/Android and iOS simulator.
|
|
86
|
+
- Commit `gradle/libs.versions.toml`, the Gradle wrapper and the Xcode project changes together.
|
|
87
|
+
|
|
88
|
+
## Never
|
|
89
|
+
|
|
90
|
+
- Never import a platform package from `commonMain`.
|
|
91
|
+
- Never put business logic in an `actual`.
|
|
92
|
+
- Never export every Gradle module to iOS as its own framework.
|
|
93
|
+
- Never let an undeclared exception cross the Kotlin/Swift boundary.
|
|
94
|
+
|
|
95
|
+
## Report back
|
|
96
|
+
|
|
97
|
+
End with: modules and source sets changed, each new expect/actual or injected interface, Gradle tasks run (and whether iOS targets actually built on this machine), and anything the iOS team must change in Xcode.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"Set up a KMP shared module so our Android and iOS apps share the networking layer",
|
|
4
|
+
"How do I use expect/actual to read the device UUID on iOS and Android?",
|
|
5
|
+
"Swift can't collect the Flow exported from our Kotlin Multiplatform framework",
|
|
6
|
+
"Should we share the UI with Compose Multiplatform or keep SwiftUI on iOS?",
|
|
7
|
+
"commonMain keeps failing because it imports java.time, how should I restructure this?"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Write a Kotlin data class with a sealed result type for this Android repository",
|
|
11
|
+
"Add a Jetpack Compose bottom sheet to the Android settings screen",
|
|
12
|
+
"Build a SwiftUI onboarding flow with a NavigationStack",
|
|
13
|
+
"Fix the Gradle error: Could not resolve com.squareup.okhttp3 in the Android app",
|
|
14
|
+
"Share business logic between iOS and Android using a Flutter plugin"
|
|
15
|
+
]
|
|
16
|
+
}
|