@heyai-rules/pilo-masterkit 1.2.2 → 3.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/.agent/agents/architect.md +211 -211
- package/.agent/agents/build-error-resolver.md +114 -114
- package/.agent/agents/chief-of-staff.md +151 -151
- package/.agent/agents/code-reviewer.md +237 -237
- package/.agent/agents/cpp-build-resolver.md +90 -90
- package/.agent/agents/cpp-reviewer.md +72 -72
- package/.agent/agents/csharp-reviewer.md +101 -0
- package/.agent/agents/dart-build-resolver.md +201 -0
- package/.agent/agents/database-reviewer.md +91 -91
- package/.agent/agents/doc-updater.md +107 -107
- package/.agent/agents/docs-lookup.md +68 -68
- package/.agent/agents/e2e-runner.md +107 -107
- package/.agent/agents/flutter-reviewer.md +243 -243
- package/.agent/agents/gan-evaluator.md +209 -0
- package/.agent/agents/gan-generator.md +131 -0
- package/.agent/agents/gan-planner.md +99 -0
- package/.agent/agents/go-build-resolver.md +94 -94
- package/.agent/agents/go-reviewer.md +76 -76
- package/.agent/agents/harness-optimizer.md +35 -35
- package/.agent/agents/healthcare-reviewer.md +83 -0
- package/.agent/agents/java-build-resolver.md +153 -153
- package/.agent/agents/java-reviewer.md +92 -92
- package/.agent/agents/kotlin-build-resolver.md +118 -118
- package/.agent/agents/kotlin-reviewer.md +159 -159
- package/.agent/agents/loop-operator.md +36 -36
- package/.agent/agents/opensource-forker.md +198 -0
- package/.agent/agents/opensource-packager.md +249 -0
- package/.agent/agents/opensource-sanitizer.md +188 -0
- package/.agent/agents/performance-optimizer.md +392 -133
- package/.agent/agents/personas/athena-agent/agent.json +10 -0
- package/.agent/agents/personas/athena-agent/athena-backend-logic-architecture-profile.md +189 -0
- package/.agent/agents/personas/athena-agent/context-files/agents.md +55 -0
- package/.agent/agents/personas/athena-agent/context-files/identity.md +23 -0
- package/.agent/agents/personas/athena-agent/context-files/soul.md +51 -0
- package/.agent/agents/personas/athena-agent/context-files/user-predefined.md +15 -0
- package/.agent/agents/personas/athena-agent/user-context-files/system/bootstrap.md +37 -0
- package/.agent/agents/personas/athena-agent/user-context-files/system/user.md +45 -0
- package/.agent/agents/personas/da-vinci-agent/agent.json +10 -0
- package/.agent/agents/personas/da-vinci-agent/context-files/agents.md +55 -0
- package/.agent/agents/personas/da-vinci-agent/context-files/identity.md +23 -0
- package/.agent/agents/personas/da-vinci-agent/context-files/soul.md +51 -0
- package/.agent/agents/personas/da-vinci-agent/context-files/user-predefined.md +15 -0
- package/.agent/agents/personas/da-vinci-agent/da-vinci-frontend-ui-ux-design-profile.md +189 -0
- package/.agent/agents/personas/da-vinci-agent/user-context-files/system/bootstrap.md +37 -0
- package/.agent/agents/personas/da-vinci-agent/user-context-files/system/user.md +45 -0
- package/.agent/agents/personas/duong-tang-agent/agent.json +10 -0
- package/.agent/agents/personas/duong-tang-agent/context-files/agents.md +55 -0
- package/.agent/agents/personas/duong-tang-agent/context-files/identity.md +23 -0
- package/.agent/agents/personas/duong-tang-agent/context-files/soul.md +51 -0
- package/.agent/agents/personas/duong-tang-agent/context-files/user-predefined.md +15 -0
- package/.agent/agents/personas/duong-tang-agent/tang-monk-quality-testing-documentation-profile.md +189 -0
- package/.agent/agents/personas/duong-tang-agent/user-context-files/system/bootstrap.md +37 -0
- package/.agent/agents/personas/duong-tang-agent/user-context-files/system/user.md +45 -0
- package/.agent/agents/personas/gia-cat-luong-agent/agent.json +10 -0
- package/.agent/agents/personas/gia-cat-luong-agent/context-files/agents.md +55 -0
- package/.agent/agents/personas/gia-cat-luong-agent/context-files/identity.md +23 -0
- package/.agent/agents/personas/gia-cat-luong-agent/context-files/soul.md +51 -0
- package/.agent/agents/personas/gia-cat-luong-agent/context-files/user-predefined.md +15 -0
- package/.agent/agents/personas/gia-cat-luong-agent/kongming-research-strategy-analysis-profile.md +189 -0
- package/.agent/agents/personas/gia-cat-luong-agent/user-context-files/system/bootstrap.md +37 -0
- package/.agent/agents/personas/gia-cat-luong-agent/user-context-files/system/user.md +45 -0
- package/.agent/agents/personas/mihata-agent/agent.json +10 -0
- package/.agent/agents/personas/mihata-agent/context-files/agents.md +55 -0
- package/.agent/agents/personas/mihata-agent/context-files/identity.md +23 -0
- package/.agent/agents/personas/mihata-agent/context-files/soul.md +51 -0
- package/.agent/agents/personas/mihata-agent/context-files/user-predefined.md +15 -0
- package/.agent/agents/personas/mihata-agent/mihata-multi-agent-orchestration-profile.md +189 -0
- package/.agent/agents/personas/mihata-agent/user-context-files/system/bootstrap.md +37 -0
- package/.agent/agents/personas/mihata-agent/user-context-files/system/user.md +45 -0
- package/.agent/agents/personas/tesla-agent/agent.json +10 -0
- package/.agent/agents/personas/tesla-agent/context-files/agents.md +55 -0
- package/.agent/agents/personas/tesla-agent/context-files/identity.md +23 -0
- package/.agent/agents/personas/tesla-agent/context-files/soul.md +51 -0
- package/.agent/agents/personas/tesla-agent/context-files/user-predefined.md +15 -0
- package/.agent/agents/personas/tesla-agent/tesla-fullstack-system-optimization-profile.md +189 -0
- package/.agent/agents/personas/tesla-agent/user-context-files/system/bootstrap.md +37 -0
- package/.agent/agents/personas/tesla-agent/user-context-files/system/user.md +45 -0
- package/.agent/agents/personas/tu-ma-y-agent/agent.json +10 -0
- package/.agent/agents/personas/tu-ma-y-agent/context-files/agents.md +55 -0
- package/.agent/agents/personas/tu-ma-y-agent/context-files/identity.md +23 -0
- package/.agent/agents/personas/tu-ma-y-agent/context-files/soul.md +51 -0
- package/.agent/agents/personas/tu-ma-y-agent/context-files/user-predefined.md +15 -0
- package/.agent/agents/personas/tu-ma-y-agent/simayi-feasibility-risk-control-profile.md +189 -0
- package/.agent/agents/personas/tu-ma-y-agent/user-context-files/system/bootstrap.md +37 -0
- package/.agent/agents/personas/tu-ma-y-agent/user-context-files/system/user.md +45 -0
- package/.agent/agents/personas/venti-agent/agent.json +10 -0
- package/.agent/agents/personas/venti-agent/context-files/agents.md +55 -0
- package/.agent/agents/personas/venti-agent/context-files/identity.md +23 -0
- package/.agent/agents/personas/venti-agent/context-files/soul.md +51 -0
- package/.agent/agents/personas/venti-agent/context-files/user-predefined.md +15 -0
- package/.agent/agents/personas/venti-agent/user-context-files/system/bootstrap.md +37 -0
- package/.agent/agents/personas/venti-agent/user-context-files/system/user.md +45 -0
- package/.agent/agents/personas/venti-agent/venti-learning-communication-mentoring-profile.md +189 -0
- package/.agent/agents/planner.md +212 -212
- package/.agent/agents/python-reviewer.md +98 -98
- package/.agent/agents/pytorch-build-resolver.md +120 -120
- package/.agent/agents/refactor-cleaner.md +85 -85
- package/.agent/agents/rust-build-resolver.md +148 -148
- package/.agent/agents/rust-reviewer.md +94 -94
- package/.agent/agents/security-reviewer.md +108 -108
- package/.agent/agents/tdd-guide.md +91 -91
- package/.agent/agents/typescript-reviewer.md +112 -112
- package/.agent/contexts/dev.md +20 -0
- package/.agent/contexts/research.md +26 -0
- package/.agent/contexts/review.md +22 -0
- package/.agent/hooks/hooks.json +395 -0
- package/.agent/hooks/readme.md +222 -0
- package/.agent/mcp-configs/mcp-servers.json +181 -0
- package/.agent/rules/common/agents.md +50 -0
- package/.agent/rules/common/code-review.md +124 -0
- package/.agent/rules/common/coding-style.md +48 -0
- package/.agent/rules/common/development-workflow.md +44 -0
- package/.agent/rules/common/git-workflow.md +24 -0
- package/.agent/rules/common/hooks.md +30 -0
- package/.agent/rules/common/patterns.md +31 -0
- package/.agent/rules/common/performance.md +55 -0
- package/.agent/rules/common/security.md +29 -0
- package/.agent/rules/common/testing.md +29 -0
- package/.agent/rules/cpp/coding-style.md +44 -0
- package/.agent/rules/cpp/hooks.md +39 -0
- package/.agent/rules/cpp/patterns.md +51 -0
- package/.agent/rules/cpp/security.md +51 -0
- package/.agent/rules/cpp/testing.md +44 -0
- package/.agent/rules/csharp/coding-style.md +72 -0
- package/.agent/rules/csharp/hooks.md +25 -0
- package/.agent/rules/csharp/patterns.md +50 -0
- package/.agent/rules/csharp/security.md +58 -0
- package/.agent/rules/csharp/testing.md +46 -0
- package/.agent/rules/dart/coding-style.md +159 -0
- package/.agent/rules/dart/hooks.md +66 -0
- package/.agent/rules/dart/patterns.md +261 -0
- package/.agent/rules/dart/security.md +135 -0
- package/.agent/rules/dart/testing.md +215 -0
- package/.agent/rules/golang/coding-style.md +32 -0
- package/.agent/rules/golang/hooks.md +17 -0
- package/.agent/rules/golang/patterns.md +45 -0
- package/.agent/rules/golang/security.md +34 -0
- package/.agent/rules/golang/testing.md +31 -0
- package/.agent/rules/java/coding-style.md +114 -0
- package/.agent/rules/java/hooks.md +18 -0
- package/.agent/rules/java/patterns.md +146 -0
- package/.agent/rules/java/security.md +100 -0
- package/.agent/rules/java/testing.md +131 -0
- package/.agent/rules/kotlin/coding-style.md +86 -0
- package/.agent/rules/kotlin/hooks.md +17 -0
- package/.agent/rules/kotlin/patterns.md +146 -0
- package/.agent/rules/kotlin/security.md +82 -0
- package/.agent/rules/kotlin/testing.md +128 -0
- package/.agent/rules/perl/coding-style.md +46 -0
- package/.agent/rules/perl/hooks.md +22 -0
- package/.agent/rules/perl/patterns.md +76 -0
- package/.agent/rules/perl/security.md +69 -0
- package/.agent/rules/perl/testing.md +54 -0
- package/.agent/rules/php/coding-style.md +40 -0
- package/.agent/rules/php/hooks.md +24 -0
- package/.agent/rules/php/patterns.md +33 -0
- package/.agent/rules/php/security.md +37 -0
- package/.agent/rules/php/testing.md +39 -0
- package/.agent/rules/python/coding-style.md +42 -0
- package/.agent/rules/python/hooks.md +19 -0
- package/.agent/rules/python/patterns.md +39 -0
- package/.agent/rules/python/security.md +30 -0
- package/.agent/rules/python/testing.md +38 -0
- package/.agent/rules/readme.md +111 -0
- package/.agent/rules/rust/coding-style.md +151 -0
- package/.agent/rules/rust/hooks.md +16 -0
- package/.agent/rules/rust/patterns.md +168 -0
- package/.agent/rules/rust/security.md +141 -0
- package/.agent/rules/rust/testing.md +154 -0
- package/.agent/rules/swift/coding-style.md +47 -0
- package/.agent/rules/swift/hooks.md +20 -0
- package/.agent/rules/swift/patterns.md +66 -0
- package/.agent/rules/swift/security.md +33 -0
- package/.agent/rules/swift/testing.md +45 -0
- package/.agent/rules/typescript/coding-style.md +199 -0
- package/.agent/rules/typescript/hooks.md +22 -0
- package/.agent/rules/typescript/patterns.md +52 -0
- package/.agent/rules/typescript/security.md +28 -0
- package/.agent/rules/typescript/testing.md +18 -0
- package/.agent/rules/web/coding-style.md +96 -0
- package/.agent/rules/web/design-quality.md +63 -0
- package/.agent/rules/web/hooks.md +120 -0
- package/.agent/rules/web/patterns.md +79 -0
- package/.agent/rules/web/performance.md +64 -0
- package/.agent/rules/web/security.md +57 -0
- package/.agent/rules/web/testing.md +55 -0
- package/.agent/rules/zh/agents.md +50 -0
- package/.agent/rules/zh/code-review.md +124 -0
- package/.agent/rules/zh/coding-style.md +48 -0
- package/.agent/rules/zh/development-workflow.md +44 -0
- package/.agent/rules/zh/git-workflow.md +24 -0
- package/.agent/rules/zh/hooks.md +30 -0
- package/.agent/rules/zh/patterns.md +31 -0
- package/.agent/rules/zh/performance.md +55 -0
- package/.agent/rules/zh/readme.md +108 -0
- package/.agent/rules/zh/security.md +29 -0
- package/.agent/rules/zh/testing.md +29 -0
- package/.agent/skills/agent-eval/SKILL.md +145 -0
- package/.agent/skills/agent-harness-construction/SKILL.md +73 -0
- package/.agent/skills/agent-payment-x402/SKILL.md +178 -0
- package/.agent/skills/agentic-engineering/SKILL.md +63 -0
- package/.agent/skills/ai-first-engineering/SKILL.md +51 -0
- package/.agent/skills/ai-regression-testing/SKILL.md +385 -0
- package/.agent/skills/android-clean-architecture/SKILL.md +339 -0
- package/.agent/skills/api-design/SKILL.md +523 -0
- package/.agent/skills/architecture-decision-records/SKILL.md +179 -0
- package/.agent/skills/article-writing/SKILL.md +79 -0
- package/.agent/skills/autonomous-agent-harness/SKILL.md +267 -0
- package/.agent/skills/autonomous-loops/SKILL.md +610 -0
- package/.agent/skills/backend-patterns/SKILL.md +598 -0
- package/.agent/skills/benchmark/SKILL.md +93 -0
- package/.agent/skills/blueprint/SKILL.md +105 -0
- package/.agent/skills/brand-voice/SKILL.md +97 -0
- package/.agent/skills/brand-voice/references/voice-profile-schema.md +55 -0
- package/.agent/skills/browser-qa/SKILL.md +87 -0
- package/.agent/skills/bun-runtime/SKILL.md +84 -0
- package/.agent/skills/canary-watch/SKILL.md +99 -0
- package/.agent/skills/carrier-relationship-management/SKILL.md +212 -0
- package/.agent/skills/ck/SKILL.md +147 -0
- package/.agent/skills/ck/commands/forget.mjs +44 -0
- package/.agent/skills/ck/commands/info.mjs +24 -0
- package/.agent/skills/ck/commands/init.mjs +143 -0
- package/.agent/skills/ck/commands/list.mjs +40 -0
- package/.agent/skills/ck/commands/migrate.mjs +202 -0
- package/.agent/skills/ck/commands/resume.mjs +36 -0
- package/.agent/skills/ck/commands/save.mjs +210 -0
- package/.agent/skills/ck/commands/shared.mjs +387 -0
- package/.agent/skills/ck/hooks/session-start.mjs +224 -0
- package/.agent/skills/claude-api/SKILL.md +337 -0
- package/.agent/skills/claude-devfleet/SKILL.md +103 -0
- package/.agent/skills/click-path-audit/SKILL.md +244 -0
- package/.agent/skills/clickhouse-io/SKILL.md +439 -0
- package/.agent/skills/codebase-onboarding/SKILL.md +233 -0
- package/.agent/skills/coding-standards/SKILL.md +530 -0
- package/.agent/skills/compose-multiplatform-patterns/SKILL.md +299 -0
- package/.agent/skills/configure-ecc/SKILL.md +367 -0
- package/.agent/skills/connections-optimizer/SKILL.md +189 -0
- package/.agent/skills/content-engine/SKILL.md +131 -0
- package/.agent/skills/content-hash-cache-pattern/SKILL.md +161 -0
- package/.agent/skills/context-budget/SKILL.md +135 -0
- package/.agent/skills/continuous-agent-loop/SKILL.md +45 -0
- package/.agent/skills/continuous-learning/SKILL.md +119 -0
- package/.agent/skills/continuous-learning/config.json +18 -0
- package/.agent/skills/continuous-learning/evaluate-session.sh +69 -0
- package/.agent/skills/continuous-learning-v2/SKILL.md +365 -0
- package/.agent/skills/continuous-learning-v2/agents/observer-loop.sh +271 -0
- package/.agent/skills/continuous-learning-v2/agents/observer.md +198 -0
- package/.agent/skills/continuous-learning-v2/agents/session-guardian.sh +150 -0
- package/.agent/skills/continuous-learning-v2/agents/start-observer.sh +244 -0
- package/.agent/skills/continuous-learning-v2/config.json +8 -0
- package/.agent/skills/continuous-learning-v2/hooks/observe.sh +428 -0
- package/.agent/skills/continuous-learning-v2/scripts/detect-project.sh +228 -0
- package/.agent/skills/continuous-learning-v2/scripts/instinct-cli.py +1426 -0
- package/.agent/skills/continuous-learning-v2/scripts/test-parse-instinct.py +984 -0
- package/.agent/skills/cost-aware-llm-pipeline/SKILL.md +183 -0
- package/.agent/skills/cpp-coding-standards/SKILL.md +723 -0
- package/.agent/skills/cpp-testing/SKILL.md +324 -0
- package/.agent/skills/crosspost/SKILL.md +111 -0
- package/.agent/skills/csharp-testing/SKILL.md +321 -0
- package/.agent/skills/customer-billing-ops/SKILL.md +140 -0
- package/.agent/skills/customs-trade-compliance/SKILL.md +263 -0
- package/.agent/skills/dart-flutter-patterns/SKILL.md +563 -0
- package/.agent/skills/data-scraper-agent/SKILL.md +764 -0
- package/.agent/skills/database-migrations/SKILL.md +429 -0
- package/.agent/skills/deep-research/SKILL.md +155 -0
- package/.agent/skills/deployment-patterns/SKILL.md +427 -0
- package/.agent/skills/design-system/SKILL.md +82 -0
- package/.agent/skills/django-patterns/SKILL.md +734 -0
- package/.agent/skills/django-security/SKILL.md +593 -0
- package/.agent/skills/django-tdd/SKILL.md +729 -0
- package/.agent/skills/django-verification/SKILL.md +469 -0
- package/.agent/skills/dmux-workflows/SKILL.md +191 -0
- package/.agent/skills/docker-patterns/SKILL.md +364 -0
- package/.agent/skills/documentation-lookup/SKILL.md +90 -0
- package/.agent/skills/dotnet-patterns/SKILL.md +321 -0
- package/.agent/skills/e2e-testing/SKILL.md +326 -0
- package/.agent/skills/energy-procurement/SKILL.md +228 -0
- package/.agent/skills/enterprise-agent-ops/SKILL.md +50 -0
- package/.agent/skills/eval-harness/SKILL.md +270 -0
- package/.agent/skills/exa-search/SKILL.md +103 -0
- package/.agent/skills/fal-ai-media/SKILL.md +284 -0
- package/.agent/skills/flutter-dart-code-review/SKILL.md +435 -0
- package/.agent/skills/foundation-models-on-device/SKILL.md +243 -0
- package/.agent/skills/frontend-patterns/SKILL.md +642 -0
- package/.agent/skills/frontend-slides/SKILL.md +184 -0
- package/.agent/skills/frontend-slides/style-presets.md +330 -0
- package/.agent/skills/gan-style-harness/SKILL.md +278 -0
- package/.agent/skills/git-workflow/SKILL.md +715 -0
- package/.agent/skills/golang-patterns/SKILL.md +674 -0
- package/.agent/skills/golang-testing/SKILL.md +720 -0
- package/.agent/skills/google-workspace-ops/SKILL.md +95 -0
- package/.agent/skills/healthcare-cdss-patterns/SKILL.md +245 -0
- package/.agent/skills/healthcare-emr-patterns/SKILL.md +159 -0
- package/.agent/skills/healthcare-eval-harness/SKILL.md +207 -0
- package/.agent/skills/healthcare-phi-compliance/SKILL.md +145 -0
- package/.agent/skills/hexagonal-architecture/SKILL.md +276 -0
- package/.agent/skills/inventory-demand-planning/SKILL.md +247 -0
- package/.agent/skills/investor-materials/SKILL.md +96 -0
- package/.agent/skills/investor-outreach/SKILL.md +91 -0
- package/.agent/skills/iterative-retrieval/SKILL.md +211 -0
- package/.agent/skills/java-coding-standards/SKILL.md +147 -0
- package/.agent/skills/jira-integration/SKILL.md +293 -0
- package/.agent/skills/jpa-patterns/SKILL.md +151 -0
- package/.agent/skills/kotlin-coroutines-flows/SKILL.md +284 -0
- package/.agent/skills/kotlin-exposed-patterns/SKILL.md +719 -0
- package/.agent/skills/kotlin-ktor-patterns/SKILL.md +689 -0
- package/.agent/skills/kotlin-patterns/SKILL.md +711 -0
- package/.agent/skills/kotlin-testing/SKILL.md +824 -0
- package/.agent/skills/laravel-patterns/SKILL.md +415 -0
- package/.agent/skills/laravel-plugin-discovery/SKILL.md +229 -0
- package/.agent/skills/laravel-security/SKILL.md +285 -0
- package/.agent/skills/laravel-tdd/SKILL.md +283 -0
- package/.agent/skills/laravel-verification/SKILL.md +179 -0
- package/.agent/skills/lead-intelligence/SKILL.md +321 -0
- package/.agent/skills/lead-intelligence/agents/enrichment-agent.md +85 -0
- package/.agent/skills/lead-intelligence/agents/mutual-mapper.md +75 -0
- package/.agent/skills/lead-intelligence/agents/outreach-drafter.md +98 -0
- package/.agent/skills/lead-intelligence/agents/signal-scorer.md +60 -0
- package/.agent/skills/liquid-glass-design/SKILL.md +279 -0
- package/.agent/skills/logistics-exception-management/SKILL.md +222 -0
- package/.agent/skills/manim-video/SKILL.md +89 -0
- package/.agent/skills/manim-video/assets/network-graph-scene.py +52 -0
- package/.agent/skills/market-research/SKILL.md +75 -0
- package/.agent/skills/mcp-builder/SKILL.md +173 -113
- package/.agent/skills/mcp-builder/license.txt +202 -0
- package/.agent/skills/mcp-builder/reference/evaluation.md +602 -0
- package/.agent/skills/mcp-builder/reference/mcp-best-practices.md +249 -0
- package/.agent/skills/mcp-builder/reference/node-mcp-server.md +970 -0
- package/.agent/skills/mcp-builder/reference/python-mcp-server.md +719 -0
- package/.agent/skills/mcp-builder/scripts/connections.py +151 -0
- package/.agent/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/.agent/skills/mcp-builder/scripts/example-evaluation.xml +22 -0
- package/.agent/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/.agent/skills/mcp-server-patterns/SKILL.md +67 -0
- package/.agent/skills/nanoclaw-repl/SKILL.md +33 -0
- package/.agent/skills/nestjs-patterns/SKILL.md +230 -0
- package/.agent/skills/nextjs-turbopack/SKILL.md +44 -0
- package/.agent/skills/nutrient-document-processing/SKILL.md +167 -0
- package/.agent/skills/nuxt4-patterns/SKILL.md +100 -0
- package/.agent/skills/openclaw-persona-forge/SKILL.md +296 -0
- package/.agent/skills/openclaw-persona-forge/gacha.py +224 -0
- package/.agent/skills/openclaw-persona-forge/gacha.sh +5 -0
- package/.agent/skills/openclaw-persona-forge/references/avatar-style.md +124 -0
- package/.agent/skills/openclaw-persona-forge/references/boundary-rules.md +53 -0
- package/.agent/skills/openclaw-persona-forge/references/error-handling.md +53 -0
- package/.agent/skills/openclaw-persona-forge/references/identity-tension.md +48 -0
- package/.agent/skills/openclaw-persona-forge/references/naming-system.md +39 -0
- package/.agent/skills/openclaw-persona-forge/references/output-template.md +166 -0
- package/.agent/skills/opensource-pipeline/SKILL.md +255 -0
- package/.agent/skills/perl-patterns/SKILL.md +504 -0
- package/.agent/skills/perl-security/SKILL.md +503 -0
- package/.agent/skills/perl-testing/SKILL.md +475 -0
- package/.agent/skills/plankton-code-quality/SKILL.md +236 -0
- package/.agent/skills/postgres-patterns/SKILL.md +147 -0
- package/.agent/skills/product-lens/SKILL.md +85 -0
- package/.agent/skills/production-scheduling/SKILL.md +238 -0
- package/.agent/skills/project-flow-ops/SKILL.md +111 -0
- package/.agent/skills/project-guidelines-example/SKILL.md +349 -0
- package/.agent/skills/prompt-optimizer/SKILL.md +397 -0
- package/.agent/skills/python-patterns/SKILL.md +622 -313
- package/.agent/skills/python-testing/SKILL.md +816 -0
- package/.agent/skills/pytorch-patterns/SKILL.md +396 -0
- package/.agent/skills/quality-nonconformance/SKILL.md +260 -0
- package/.agent/skills/ralphinho-rfc-pipeline/SKILL.md +67 -0
- package/.agent/skills/regex-vs-llm-structured-text/SKILL.md +220 -0
- package/.agent/skills/remotion-video-creation/SKILL.md +43 -0
- package/.agent/skills/remotion-video-creation/rules/3d.md +86 -0
- package/.agent/skills/remotion-video-creation/rules/animations.md +29 -0
- package/.agent/skills/remotion-video-creation/rules/assets/charts-bar-chart.tsx +173 -0
- package/.agent/skills/remotion-video-creation/rules/assets/text-animations-typewriter.tsx +100 -0
- package/.agent/skills/remotion-video-creation/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/.agent/skills/remotion-video-creation/rules/assets.md +78 -0
- package/.agent/skills/remotion-video-creation/rules/audio.md +172 -0
- package/.agent/skills/remotion-video-creation/rules/calculate-metadata.md +104 -0
- package/.agent/skills/remotion-video-creation/rules/can-decode.md +75 -0
- package/.agent/skills/remotion-video-creation/rules/charts.md +58 -0
- package/.agent/skills/remotion-video-creation/rules/compositions.md +146 -0
- package/.agent/skills/remotion-video-creation/rules/display-captions.md +126 -0
- package/.agent/skills/remotion-video-creation/rules/extract-frames.md +229 -0
- package/.agent/skills/remotion-video-creation/rules/fonts.md +152 -0
- package/.agent/skills/remotion-video-creation/rules/get-audio-duration.md +58 -0
- package/.agent/skills/remotion-video-creation/rules/get-video-dimensions.md +68 -0
- package/.agent/skills/remotion-video-creation/rules/get-video-duration.md +58 -0
- package/.agent/skills/remotion-video-creation/rules/gifs.md +138 -0
- package/.agent/skills/remotion-video-creation/rules/images.md +130 -0
- package/.agent/skills/remotion-video-creation/rules/import-srt-captions.md +67 -0
- package/.agent/skills/remotion-video-creation/rules/lottie.md +67 -0
- package/.agent/skills/remotion-video-creation/rules/measuring-dom-nodes.md +34 -0
- package/.agent/skills/remotion-video-creation/rules/measuring-text.md +143 -0
- package/.agent/skills/remotion-video-creation/rules/sequencing.md +106 -0
- package/.agent/skills/remotion-video-creation/rules/tailwind.md +11 -0
- package/.agent/skills/remotion-video-creation/rules/text-animations.md +20 -0
- package/.agent/skills/remotion-video-creation/rules/timing.md +179 -0
- package/.agent/skills/remotion-video-creation/rules/transcribe-captions.md +19 -0
- package/.agent/skills/remotion-video-creation/rules/transitions.md +122 -0
- package/.agent/skills/remotion-video-creation/rules/trimming.md +52 -0
- package/.agent/skills/remotion-video-creation/rules/videos.md +171 -0
- package/.agent/skills/repo-scan/SKILL.md +78 -0
- package/.agent/skills/returns-reverse-logistics/SKILL.md +240 -0
- package/.agent/skills/rules-distill/SKILL.md +264 -0
- package/.agent/skills/rules-distill/scripts/scan-rules.sh +58 -0
- package/.agent/skills/rules-distill/scripts/scan-skills.sh +129 -0
- package/.agent/skills/rust-patterns/SKILL.md +499 -0
- package/.agent/skills/rust-testing/SKILL.md +500 -0
- package/.agent/skills/safety-guard/SKILL.md +75 -0
- package/.agent/skills/santa-method/SKILL.md +306 -0
- package/.agent/skills/search-first/SKILL.md +161 -0
- package/.agent/skills/security-review/SKILL.md +495 -0
- package/.agent/skills/security-review/cloud-infrastructure-security.md +361 -0
- package/.agent/skills/security-scan/SKILL.md +165 -0
- package/.agent/skills/skill-comply/SKILL.md +58 -0
- package/.agent/skills/skill-comply/fixtures/compliant-trace.jsonl +5 -0
- package/.agent/skills/skill-comply/fixtures/noncompliant-trace.jsonl +3 -0
- package/.agent/skills/skill-comply/fixtures/tdd-spec.yaml +44 -0
- package/.agent/skills/skill-comply/prompts/classifier.md +24 -0
- package/.agent/skills/skill-comply/prompts/scenario-generator.md +62 -0
- package/.agent/skills/skill-comply/prompts/spec-generator.md +42 -0
- package/.agent/skills/skill-comply/pyproject.toml +15 -0
- package/.agent/skills/skill-comply/scripts/classifier.py +85 -0
- package/.agent/skills/skill-comply/scripts/grader.py +122 -0
- package/.agent/skills/skill-comply/scripts/init.py +0 -0
- package/.agent/skills/skill-comply/scripts/parser.py +107 -0
- package/.agent/skills/skill-comply/scripts/report.py +170 -0
- package/.agent/skills/skill-comply/scripts/run.py +127 -0
- package/.agent/skills/skill-comply/scripts/runner.py +161 -0
- package/.agent/skills/skill-comply/scripts/scenario-generator.py +70 -0
- package/.agent/skills/skill-comply/scripts/spec-generator.py +72 -0
- package/.agent/skills/skill-comply/scripts/utils.py +13 -0
- package/.agent/skills/skill-comply/tests/test-grader.py +137 -0
- package/.agent/skills/skill-comply/tests/test-parser.py +90 -0
- package/.agent/skills/skill-creator/SKILL.md +485 -0
- package/.agent/skills/skill-creator/agents/analyzer.md +274 -0
- package/.agent/skills/skill-creator/agents/comparator.md +202 -0
- package/.agent/skills/skill-creator/agents/grader.md +223 -0
- package/.agent/skills/skill-creator/assets/eval-review.html +146 -0
- package/.agent/skills/skill-creator/eval-viewer/generate-review.py +471 -0
- package/.agent/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/.agent/skills/skill-creator/license.txt +202 -0
- package/.agent/skills/skill-creator/references/schemas.md +430 -0
- package/.agent/skills/skill-creator/scripts/aggregate-benchmark.py +401 -0
- package/.agent/skills/skill-creator/scripts/generate-report.py +326 -0
- package/.agent/skills/skill-creator/scripts/improve-description.py +247 -0
- package/.agent/skills/skill-creator/scripts/init.py +0 -0
- package/.agent/skills/skill-creator/scripts/package-skill.py +136 -0
- package/.agent/skills/skill-creator/scripts/quick-validate.py +103 -0
- package/.agent/skills/skill-creator/scripts/run-eval.py +310 -0
- package/.agent/skills/skill-creator/scripts/run-loop.py +328 -0
- package/.agent/skills/skill-creator/scripts/utils.py +47 -0
- package/.agent/skills/skill-stocktake/SKILL.md +193 -0
- package/.agent/skills/skill-stocktake/scripts/quick-diff.sh +87 -0
- package/.agent/skills/skill-stocktake/scripts/save-results.sh +56 -0
- package/.agent/skills/skill-stocktake/scripts/scan.sh +170 -0
- package/.agent/skills/social-graph-ranker/SKILL.md +154 -0
- package/.agent/skills/springboot-patterns/SKILL.md +314 -0
- package/.agent/skills/springboot-security/SKILL.md +272 -0
- package/.agent/skills/springboot-tdd/SKILL.md +158 -0
- package/.agent/skills/springboot-verification/SKILL.md +231 -0
- package/.agent/skills/strategic-compact/SKILL.md +131 -0
- package/.agent/skills/strategic-compact/suggest-compact.sh +54 -0
- package/.agent/skills/swift-actor-persistence/SKILL.md +143 -0
- package/.agent/skills/swift-concurrency-6-2/SKILL.md +216 -0
- package/.agent/skills/swift-protocol-di-testing/SKILL.md +190 -0
- package/.agent/skills/swiftui-patterns/SKILL.md +259 -0
- package/.agent/skills/tdd-workflow/SKILL.md +412 -98
- package/.agent/skills/team-builder/SKILL.md +168 -0
- package/.agent/skills/token-budget-advisor/SKILL.md +133 -0
- package/.agent/skills/ui-demo/SKILL.md +465 -0
- package/.agent/skills/ui-ux-pro-max/data/charts.csv +26 -26
- package/.agent/skills/ui-ux-pro-max/data/colors.csv +97 -97
- package/.agent/skills/ui-ux-pro-max/data/landing.csv +28 -28
- package/.agent/skills/ui-ux-pro-max/data/products.csv +96 -96
- package/.agent/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -53
- package/.agent/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -56
- package/.agent/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -53
- package/.agent/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -52
- package/.agent/skills/ui-ux-pro-max/data/stacks/react.csv +54 -54
- package/.agent/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -54
- package/.agent/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -51
- package/.agent/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -50
- package/.agent/skills/ui-ux-pro-max/data/styles.csv +68 -68
- package/.agent/skills/ui-ux-pro-max/data/ux-guidelines.csv +99 -99
- package/.agent/skills/ui-ux-pro-max/scripts/search.py +114 -114
- package/.agent/skills/verification-loop/SKILL.md +126 -0
- package/.agent/skills/video-editing/SKILL.md +310 -0
- package/.agent/skills/videodb/SKILL.md +374 -0
- package/.agent/skills/videodb/reference/api-reference.md +550 -0
- package/.agent/skills/videodb/reference/capture-reference.md +407 -0
- package/.agent/skills/videodb/reference/capture.md +101 -0
- package/.agent/skills/videodb/reference/editor.md +443 -0
- package/.agent/skills/videodb/reference/generative.md +331 -0
- package/.agent/skills/videodb/reference/rtstream-reference.md +564 -0
- package/.agent/skills/videodb/reference/rtstream.md +65 -0
- package/.agent/skills/videodb/reference/search.md +230 -0
- package/.agent/skills/videodb/reference/streaming.md +406 -0
- package/.agent/skills/videodb/reference/use-cases.md +118 -0
- package/.agent/skills/videodb/scripts/ws-listener.py +282 -0
- package/.agent/skills/visa-doc-translate/SKILL.md +117 -0
- package/.agent/skills/visa-doc-translate/readme.md +86 -0
- package/.agent/skills/workspace-surface-audit/SKILL.md +125 -0
- package/.agent/skills/x-api/SKILL.md +230 -0
- package/.agent/tasks/two-track-merge-contract.md +29 -0
- package/.agent/workflows/aside.md +164 -164
- package/.agent/workflows/build-fix.md +62 -62
- package/.agent/workflows/checkpoint.md +74 -74
- package/.agent/workflows/claw.md +23 -51
- package/.agent/workflows/clean-memory.md +34 -0
- package/.agent/workflows/code-review.md +289 -40
- package/.agent/workflows/context-budget.md +23 -29
- package/.agent/workflows/cpp-build.md +173 -173
- package/.agent/workflows/cpp-review.md +132 -132
- package/.agent/workflows/cpp-test.md +251 -251
- package/.agent/workflows/devfleet.md +23 -92
- package/.agent/workflows/docs.md +23 -31
- package/.agent/workflows/e2e.md +268 -365
- package/.agent/workflows/eval.md +23 -120
- package/.agent/workflows/evolve.md +178 -178
- package/.agent/workflows/flutter-build.md +164 -0
- package/.agent/workflows/flutter-review.md +116 -0
- package/.agent/workflows/flutter-test.md +144 -0
- package/.agent/workflows/gan-build.md +99 -0
- package/.agent/workflows/gan-design.md +35 -0
- package/.agent/workflows/go-build.md +183 -183
- package/.agent/workflows/go-review.md +148 -148
- package/.agent/workflows/go-test.md +268 -268
- package/.agent/workflows/gradle-build.md +70 -70
- package/.agent/workflows/harness-audit.md +73 -71
- package/.agent/workflows/instinct-export.md +66 -66
- package/.agent/workflows/instinct-import.md +114 -114
- package/.agent/workflows/instinct-status.md +59 -59
- package/.agent/workflows/jira.md +106 -0
- package/.agent/workflows/kotlin-build.md +174 -174
- package/.agent/workflows/kotlin-review.md +140 -140
- package/.agent/workflows/kotlin-test.md +312 -312
- package/.agent/workflows/learn-eval.md +116 -116
- package/.agent/workflows/learn.md +70 -70
- package/.agent/workflows/loop-start.md +32 -32
- package/.agent/workflows/loop-status.md +24 -24
- package/.agent/workflows/model-route.md +26 -26
- package/.agent/workflows/multi-backend.md +158 -158
- package/.agent/workflows/multi-execute.md +315 -315
- package/.agent/workflows/multi-frontend.md +158 -158
- package/.agent/workflows/multi-plan.md +268 -268
- package/.agent/workflows/multi-workflow.md +191 -191
- package/.agent/workflows/orchestrate.md +135 -231
- package/.agent/workflows/plan.md +117 -115
- package/.agent/workflows/pm2.md +272 -272
- package/.agent/workflows/projects.md +39 -39
- package/.agent/workflows/promote.md +41 -41
- package/.agent/workflows/prompt-optimize.md +23 -38
- package/.agent/workflows/prp-commit.md +112 -0
- package/.agent/workflows/prp-implement.md +385 -0
- package/.agent/workflows/prp-plan.md +502 -0
- package/.agent/workflows/prp-pr.md +184 -0
- package/.agent/workflows/prp-prd.md +447 -0
- package/.agent/workflows/prune.md +31 -31
- package/.agent/workflows/python-review.md +297 -297
- package/.agent/workflows/quality-gate.md +29 -29
- package/.agent/workflows/refactor-clean.md +80 -80
- package/.agent/workflows/resume-session.md +156 -156
- package/.agent/workflows/rules-distill.md +20 -11
- package/.agent/workflows/rust-build.md +187 -187
- package/.agent/workflows/rust-review.md +142 -142
- package/.agent/workflows/rust-test.md +308 -308
- package/.agent/workflows/santa-loop.md +175 -0
- package/.agent/workflows/save-session.md +275 -275
- package/.agent/workflows/sessions.md +333 -333
- package/.agent/workflows/setup-pm.md +80 -80
- package/.agent/workflows/skill-create.md +174 -174
- package/.agent/workflows/skill-health.md +54 -54
- package/.agent/workflows/tdd.md +231 -328
- package/.agent/workflows/test-coverage.md +69 -69
- package/.agent/workflows/update-codemaps.md +72 -72
- package/.agent/workflows/update-docs.md +84 -84
- package/.agent/workflows/verify.md +23 -59
- package/LICENSE +176 -176
- package/README.md +28 -20
- package/RELEASE.md +32 -36
- package/package.json +87 -79
- package/scripts/release-check.js +55 -55
- package/src/bin/cli.js +399 -53
- package/src/lib/installer.js +360 -114
- package/src/lib/manifests/stacks.js +122 -0
- package/src/lib/slash-commands.js +28 -0
- package/src/templates/claude/CLAUDE.en.md +42 -0
- package/src/templates/claude/CLAUDE.md +42 -0
- package/src/templates/claude/CLAUDE.vi.md +42 -0
- package/src/templates/codex/AGENTS.en.md +40 -0
- package/src/templates/codex/AGENTS.md +40 -0
- package/src/templates/codex/AGENTS.vi.md +40 -0
- package/src/templates/cursor/pilo-masterkit.mdc +20 -0
- package/src/templates/gemini/GEMINI.en.md +56 -0
- package/src/templates/gemini/GEMINI.md +56 -0
- package/src/templates/gemini/GEMINI.vi.md +56 -0
- package/src/templates/github/copilot-instructions.md +16 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.java"
|
|
4
|
+
---
|
|
5
|
+
# Java Security
|
|
6
|
+
|
|
7
|
+
> This file extends [common/security.md](../common/security.md) with Java-specific content.
|
|
8
|
+
|
|
9
|
+
## Secrets Management
|
|
10
|
+
|
|
11
|
+
- Never hardcode API keys, tokens, or credentials in source code
|
|
12
|
+
- Use environment variables: `System.getenv("API_KEY")`
|
|
13
|
+
- Use a secret manager (Vault, AWS Secrets Manager) for production secrets
|
|
14
|
+
- Keep local config files with secrets in `.gitignore`
|
|
15
|
+
|
|
16
|
+
```java
|
|
17
|
+
// BAD
|
|
18
|
+
private static final String API_KEY = "sk-abc123...";
|
|
19
|
+
|
|
20
|
+
// GOOD — environment variable
|
|
21
|
+
String apiKey = System.getenv("PAYMENT_API_KEY");
|
|
22
|
+
Objects.requireNonNull(apiKey, "PAYMENT_API_KEY must be set");
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## SQL Injection Prevention
|
|
26
|
+
|
|
27
|
+
- Always use parameterized queries — never concatenate user input into SQL
|
|
28
|
+
- Use `PreparedStatement` or your framework's parameterized query API
|
|
29
|
+
- Validate and sanitize any input used in native queries
|
|
30
|
+
|
|
31
|
+
```java
|
|
32
|
+
// BAD — SQL injection via string concatenation
|
|
33
|
+
Statement stmt = conn.createStatement();
|
|
34
|
+
String sql = "SELECT * FROM orders WHERE name = '" + name + "'";
|
|
35
|
+
stmt.executeQuery(sql);
|
|
36
|
+
|
|
37
|
+
// GOOD — PreparedStatement with parameterized query
|
|
38
|
+
PreparedStatement ps = conn.prepareStatement("SELECT * FROM orders WHERE name = ?");
|
|
39
|
+
ps.setString(1, name);
|
|
40
|
+
|
|
41
|
+
// GOOD — JDBC template
|
|
42
|
+
jdbcTemplate.query("SELECT * FROM orders WHERE name = ?", mapper, name);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Input Validation
|
|
46
|
+
|
|
47
|
+
- Validate all user input at system boundaries before processing
|
|
48
|
+
- Use Bean Validation (`@NotNull`, `@NotBlank`, `@Size`) on DTOs when using a validation framework
|
|
49
|
+
- Sanitize file paths and user-provided strings before use
|
|
50
|
+
- Reject input that fails validation with clear error messages
|
|
51
|
+
|
|
52
|
+
```java
|
|
53
|
+
// Validate manually in plain Java
|
|
54
|
+
public Order createOrder(String customerName, BigDecimal amount) {
|
|
55
|
+
if (customerName == null || customerName.isBlank()) {
|
|
56
|
+
throw new IllegalArgumentException("Customer name is required");
|
|
57
|
+
}
|
|
58
|
+
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
|
|
59
|
+
throw new IllegalArgumentException("Amount must be positive");
|
|
60
|
+
}
|
|
61
|
+
return new Order(customerName, amount);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Authentication and Authorization
|
|
66
|
+
|
|
67
|
+
- Never implement custom auth crypto — use established libraries
|
|
68
|
+
- Store passwords with bcrypt or Argon2, never MD5/SHA1
|
|
69
|
+
- Enforce authorization checks at service boundaries
|
|
70
|
+
- Clear sensitive data from logs — never log passwords, tokens, or PII
|
|
71
|
+
|
|
72
|
+
## Dependency Security
|
|
73
|
+
|
|
74
|
+
- Run `mvn dependency:tree` or `./gradlew dependencies` to audit transitive dependencies
|
|
75
|
+
- Use OWASP Dependency-Check or Snyk to scan for known CVEs
|
|
76
|
+
- Keep dependencies updated — set up Dependabot or Renovate
|
|
77
|
+
|
|
78
|
+
## Error Messages
|
|
79
|
+
|
|
80
|
+
- Never expose stack traces, internal paths, or SQL errors in API responses
|
|
81
|
+
- Map exceptions to safe, generic client messages at handler boundaries
|
|
82
|
+
- Log detailed errors server-side; return generic messages to clients
|
|
83
|
+
|
|
84
|
+
```java
|
|
85
|
+
// Log the detail, return a generic message
|
|
86
|
+
try {
|
|
87
|
+
return orderService.findById(id);
|
|
88
|
+
} catch (OrderNotFoundException ex) {
|
|
89
|
+
log.warn("Order not found: id={}", id);
|
|
90
|
+
return ApiResponse.error("Resource not found"); // generic, no internals
|
|
91
|
+
} catch (Exception ex) {
|
|
92
|
+
log.error("Unexpected error processing order id={}", id, ex);
|
|
93
|
+
return ApiResponse.error("Internal server error"); // never expose ex.getMessage()
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## References
|
|
98
|
+
|
|
99
|
+
See skill: `springboot-security` for Spring Security authentication and authorization patterns.
|
|
100
|
+
See skill: `security-review` for general security checklists.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.java"
|
|
4
|
+
---
|
|
5
|
+
# Java Testing
|
|
6
|
+
|
|
7
|
+
> This file extends [common/testing.md](../common/testing.md) with Java-specific content.
|
|
8
|
+
|
|
9
|
+
## Test Framework
|
|
10
|
+
|
|
11
|
+
- **JUnit 5** (`@Test`, `@ParameterizedTest`, `@Nested`, `@DisplayName`)
|
|
12
|
+
- **AssertJ** for fluent assertions (`assertThat(result).isEqualTo(expected)`)
|
|
13
|
+
- **Mockito** for mocking dependencies
|
|
14
|
+
- **Testcontainers** for integration tests requiring databases or services
|
|
15
|
+
|
|
16
|
+
## Test Organization
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
src/test/java/com/example/app/
|
|
20
|
+
service/ # Unit tests for service layer
|
|
21
|
+
controller/ # Web layer / API tests
|
|
22
|
+
repository/ # Data access tests
|
|
23
|
+
integration/ # Cross-layer integration tests
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Mirror the `src/main/java` package structure in `src/test/java`.
|
|
27
|
+
|
|
28
|
+
## Unit Test Pattern
|
|
29
|
+
|
|
30
|
+
```java
|
|
31
|
+
@ExtendWith(MockitoExtension.class)
|
|
32
|
+
class OrderServiceTest {
|
|
33
|
+
|
|
34
|
+
@Mock
|
|
35
|
+
private OrderRepository orderRepository;
|
|
36
|
+
|
|
37
|
+
private OrderService orderService;
|
|
38
|
+
|
|
39
|
+
@BeforeEach
|
|
40
|
+
void setUp() {
|
|
41
|
+
orderService = new OrderService(orderRepository);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Test
|
|
45
|
+
@DisplayName("findById returns order when exists")
|
|
46
|
+
void findById_existingOrder_returnsOrder() {
|
|
47
|
+
var order = new Order(1L, "Alice", BigDecimal.TEN);
|
|
48
|
+
when(orderRepository.findById(1L)).thenReturn(Optional.of(order));
|
|
49
|
+
|
|
50
|
+
var result = orderService.findById(1L);
|
|
51
|
+
|
|
52
|
+
assertThat(result.customerName()).isEqualTo("Alice");
|
|
53
|
+
verify(orderRepository).findById(1L);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Test
|
|
57
|
+
@DisplayName("findById throws when order not found")
|
|
58
|
+
void findById_missingOrder_throws() {
|
|
59
|
+
when(orderRepository.findById(99L)).thenReturn(Optional.empty());
|
|
60
|
+
|
|
61
|
+
assertThatThrownBy(() -> orderService.findById(99L))
|
|
62
|
+
.isInstanceOf(OrderNotFoundException.class)
|
|
63
|
+
.hasMessageContaining("99");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Parameterized Tests
|
|
69
|
+
|
|
70
|
+
```java
|
|
71
|
+
@ParameterizedTest
|
|
72
|
+
@CsvSource({
|
|
73
|
+
"100.00, 10, 90.00",
|
|
74
|
+
"50.00, 0, 50.00",
|
|
75
|
+
"200.00, 25, 150.00"
|
|
76
|
+
})
|
|
77
|
+
@DisplayName("discount applied correctly")
|
|
78
|
+
void applyDiscount(BigDecimal price, int pct, BigDecimal expected) {
|
|
79
|
+
assertThat(PricingUtils.discount(price, pct)).isEqualByComparingTo(expected);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Integration Tests
|
|
84
|
+
|
|
85
|
+
Use Testcontainers for real database integration:
|
|
86
|
+
|
|
87
|
+
```java
|
|
88
|
+
@Testcontainers
|
|
89
|
+
class OrderRepositoryIT {
|
|
90
|
+
|
|
91
|
+
@Container
|
|
92
|
+
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");
|
|
93
|
+
|
|
94
|
+
private OrderRepository repository;
|
|
95
|
+
|
|
96
|
+
@BeforeEach
|
|
97
|
+
void setUp() {
|
|
98
|
+
var dataSource = new PGSimpleDataSource();
|
|
99
|
+
dataSource.setUrl(postgres.getJdbcUrl());
|
|
100
|
+
dataSource.setUser(postgres.getUsername());
|
|
101
|
+
dataSource.setPassword(postgres.getPassword());
|
|
102
|
+
repository = new JdbcOrderRepository(dataSource);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@Test
|
|
106
|
+
void save_and_findById() {
|
|
107
|
+
var saved = repository.save(new Order(null, "Bob", BigDecimal.ONE));
|
|
108
|
+
var found = repository.findById(saved.getId());
|
|
109
|
+
assertThat(found).isPresent();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
For Spring Boot integration tests, see skill: `springboot-tdd`.
|
|
115
|
+
|
|
116
|
+
## Test Naming
|
|
117
|
+
|
|
118
|
+
Use descriptive names with `@DisplayName`:
|
|
119
|
+
- `methodName_scenario_expectedBehavior()` for method names
|
|
120
|
+
- `@DisplayName("human-readable description")` for reports
|
|
121
|
+
|
|
122
|
+
## Coverage
|
|
123
|
+
|
|
124
|
+
- Target 80%+ line coverage
|
|
125
|
+
- Use JaCoCo for coverage reporting
|
|
126
|
+
- Focus on service and domain logic — skip trivial getters/config classes
|
|
127
|
+
|
|
128
|
+
## References
|
|
129
|
+
|
|
130
|
+
See skill: `springboot-tdd` for Spring Boot TDD patterns with MockMvc and Testcontainers.
|
|
131
|
+
See skill: `java-coding-standards` for testing expectations.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.kt"
|
|
4
|
+
- "**/*.kts"
|
|
5
|
+
---
|
|
6
|
+
# Kotlin Coding Style
|
|
7
|
+
|
|
8
|
+
> This file extends [common/coding-style.md](../common/coding-style.md) with Kotlin-specific content.
|
|
9
|
+
|
|
10
|
+
## Formatting
|
|
11
|
+
|
|
12
|
+
- **ktlint** or **Detekt** for style enforcement
|
|
13
|
+
- Official Kotlin code style (`kotlin.code.style=official` in `gradle.properties`)
|
|
14
|
+
|
|
15
|
+
## Immutability
|
|
16
|
+
|
|
17
|
+
- Prefer `val` over `var` — default to `val` and only use `var` when mutation is required
|
|
18
|
+
- Use `data class` for value types; use immutable collections (`List`, `Map`, `Set`) in public APIs
|
|
19
|
+
- Copy-on-write for state updates: `state.copy(field = newValue)`
|
|
20
|
+
|
|
21
|
+
## Naming
|
|
22
|
+
|
|
23
|
+
Follow Kotlin conventions:
|
|
24
|
+
- `camelCase` for functions and properties
|
|
25
|
+
- `PascalCase` for classes, interfaces, objects, and type aliases
|
|
26
|
+
- `SCREAMING_SNAKE_CASE` for constants (`const val` or `@JvmStatic`)
|
|
27
|
+
- Prefix interfaces with behavior, not `I`: `Clickable` not `IClickable`
|
|
28
|
+
|
|
29
|
+
## Null Safety
|
|
30
|
+
|
|
31
|
+
- Never use `!!` — prefer `?.`, `?:`, `requireNotNull()`, or `checkNotNull()`
|
|
32
|
+
- Use `?.let {}` for scoped null-safe operations
|
|
33
|
+
- Return nullable types from functions that can legitimately have no result
|
|
34
|
+
|
|
35
|
+
```kotlin
|
|
36
|
+
// BAD
|
|
37
|
+
val name = user!!.name
|
|
38
|
+
|
|
39
|
+
// GOOD
|
|
40
|
+
val name = user?.name ?: "Unknown"
|
|
41
|
+
val name = requireNotNull(user) { "User must be set before accessing name" }.name
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Sealed Types
|
|
45
|
+
|
|
46
|
+
Use sealed classes/interfaces to model closed state hierarchies:
|
|
47
|
+
|
|
48
|
+
```kotlin
|
|
49
|
+
sealed interface UiState<out T> {
|
|
50
|
+
data object Loading : UiState<Nothing>
|
|
51
|
+
data class Success<T>(val data: T) : UiState<T>
|
|
52
|
+
data class Error(val message: String) : UiState<Nothing>
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Always use exhaustive `when` with sealed types — no `else` branch.
|
|
57
|
+
|
|
58
|
+
## Extension Functions
|
|
59
|
+
|
|
60
|
+
Use extension functions for utility operations, but keep them discoverable:
|
|
61
|
+
- Place in a file named after the receiver type (`StringExt.kt`, `FlowExt.kt`)
|
|
62
|
+
- Keep scope limited — don't add extensions to `Any` or overly generic types
|
|
63
|
+
|
|
64
|
+
## Scope Functions
|
|
65
|
+
|
|
66
|
+
Use the right scope function:
|
|
67
|
+
- `let` — null check + transform: `user?.let { greet(it) }`
|
|
68
|
+
- `run` — compute a result using receiver: `service.run { fetch(config) }`
|
|
69
|
+
- `apply` — configure an object: `builder.apply { timeout = 30 }`
|
|
70
|
+
- `also` — side effects: `result.also { log(it) }`
|
|
71
|
+
- Avoid deep nesting of scope functions (max 2 levels)
|
|
72
|
+
|
|
73
|
+
## Error Handling
|
|
74
|
+
|
|
75
|
+
- Use `Result<T>` or custom sealed types
|
|
76
|
+
- Use `runCatching {}` for wrapping throwable code
|
|
77
|
+
- Never catch `CancellationException` — always rethrow it
|
|
78
|
+
- Avoid `try-catch` for control flow
|
|
79
|
+
|
|
80
|
+
```kotlin
|
|
81
|
+
// BAD — using exceptions for control flow
|
|
82
|
+
val user = try { repository.getUser(id) } catch (e: NotFoundException) { null }
|
|
83
|
+
|
|
84
|
+
// GOOD — nullable return
|
|
85
|
+
val user: User? = repository.findUser(id)
|
|
86
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.kt"
|
|
4
|
+
- "**/*.kts"
|
|
5
|
+
- "**/build.gradle.kts"
|
|
6
|
+
---
|
|
7
|
+
# Kotlin Hooks
|
|
8
|
+
|
|
9
|
+
> This file extends [common/hooks.md](../common/hooks.md) with Kotlin-specific content.
|
|
10
|
+
|
|
11
|
+
## PostToolUse Hooks
|
|
12
|
+
|
|
13
|
+
Configure in `~/.claude/settings.json`:
|
|
14
|
+
|
|
15
|
+
- **ktfmt/ktlint**: Auto-format `.kt` and `.kts` files after edit
|
|
16
|
+
- **detekt**: Run static analysis after editing Kotlin files
|
|
17
|
+
- **./gradlew build**: Verify compilation after changes
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.kt"
|
|
4
|
+
- "**/*.kts"
|
|
5
|
+
---
|
|
6
|
+
# Kotlin Patterns
|
|
7
|
+
|
|
8
|
+
> This file extends [common/patterns.md](../common/patterns.md) with Kotlin and Android/KMP-specific content.
|
|
9
|
+
|
|
10
|
+
## Dependency Injection
|
|
11
|
+
|
|
12
|
+
Prefer constructor injection. Use Koin (KMP) or Hilt (Android-only):
|
|
13
|
+
|
|
14
|
+
```kotlin
|
|
15
|
+
// Koin — declare modules
|
|
16
|
+
val dataModule = module {
|
|
17
|
+
single<ItemRepository> { ItemRepositoryImpl(get(), get()) }
|
|
18
|
+
factory { GetItemsUseCase(get()) }
|
|
19
|
+
viewModelOf(::ItemListViewModel)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Hilt — annotations
|
|
23
|
+
@HiltViewModel
|
|
24
|
+
class ItemListViewModel @Inject constructor(
|
|
25
|
+
private val getItems: GetItemsUseCase
|
|
26
|
+
) : ViewModel()
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## ViewModel Pattern
|
|
30
|
+
|
|
31
|
+
Single state object, event sink, one-way data flow:
|
|
32
|
+
|
|
33
|
+
```kotlin
|
|
34
|
+
data class ScreenState(
|
|
35
|
+
val items: List<Item> = emptyList(),
|
|
36
|
+
val isLoading: Boolean = false
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
class ScreenViewModel(private val useCase: GetItemsUseCase) : ViewModel() {
|
|
40
|
+
private val _state = MutableStateFlow(ScreenState())
|
|
41
|
+
val state = _state.asStateFlow()
|
|
42
|
+
|
|
43
|
+
fun onEvent(event: ScreenEvent) {
|
|
44
|
+
when (event) {
|
|
45
|
+
is ScreenEvent.Load -> load()
|
|
46
|
+
is ScreenEvent.Delete -> delete(event.id)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Repository Pattern
|
|
53
|
+
|
|
54
|
+
- `suspend` functions return `Result<T>` or custom error type
|
|
55
|
+
- `Flow` for reactive streams
|
|
56
|
+
- Coordinate local + remote data sources
|
|
57
|
+
|
|
58
|
+
```kotlin
|
|
59
|
+
interface ItemRepository {
|
|
60
|
+
suspend fun getById(id: String): Result<Item>
|
|
61
|
+
suspend fun getAll(): Result<List<Item>>
|
|
62
|
+
fun observeAll(): Flow<List<Item>>
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## UseCase Pattern
|
|
67
|
+
|
|
68
|
+
Single responsibility, `operator fun invoke`:
|
|
69
|
+
|
|
70
|
+
```kotlin
|
|
71
|
+
class GetItemUseCase(private val repository: ItemRepository) {
|
|
72
|
+
suspend operator fun invoke(id: String): Result<Item> {
|
|
73
|
+
return repository.getById(id)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
class GetItemsUseCase(private val repository: ItemRepository) {
|
|
78
|
+
suspend operator fun invoke(): Result<List<Item>> {
|
|
79
|
+
return repository.getAll()
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## expect/actual (KMP)
|
|
85
|
+
|
|
86
|
+
Use for platform-specific implementations:
|
|
87
|
+
|
|
88
|
+
```kotlin
|
|
89
|
+
// commonMain
|
|
90
|
+
expect fun platformName(): String
|
|
91
|
+
expect class SecureStorage {
|
|
92
|
+
fun save(key: String, value: String)
|
|
93
|
+
fun get(key: String): String?
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// androidMain
|
|
97
|
+
actual fun platformName(): String = "Android"
|
|
98
|
+
actual class SecureStorage {
|
|
99
|
+
actual fun save(key: String, value: String) { /* EncryptedSharedPreferences */ }
|
|
100
|
+
actual fun get(key: String): String? = null /* ... */
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// iosMain
|
|
104
|
+
actual fun platformName(): String = "iOS"
|
|
105
|
+
actual class SecureStorage {
|
|
106
|
+
actual fun save(key: String, value: String) { /* Keychain */ }
|
|
107
|
+
actual fun get(key: String): String? = null /* ... */
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Coroutine Patterns
|
|
112
|
+
|
|
113
|
+
- Use `viewModelScope` in ViewModels, `coroutineScope` for structured child work
|
|
114
|
+
- Use `stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), initialValue)` for StateFlow from cold Flows
|
|
115
|
+
- Use `supervisorScope` when child failures should be independent
|
|
116
|
+
|
|
117
|
+
## Builder Pattern with DSL
|
|
118
|
+
|
|
119
|
+
```kotlin
|
|
120
|
+
class HttpClientConfig {
|
|
121
|
+
var baseUrl: String = ""
|
|
122
|
+
var timeout: Long = 30_000
|
|
123
|
+
private val interceptors = mutableListOf<Interceptor>()
|
|
124
|
+
|
|
125
|
+
fun interceptor(block: () -> Interceptor) {
|
|
126
|
+
interceptors.add(block())
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fun httpClient(block: HttpClientConfig.() -> Unit): HttpClient {
|
|
131
|
+
val config = HttpClientConfig().apply(block)
|
|
132
|
+
return HttpClient(config)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Usage
|
|
136
|
+
val client = httpClient {
|
|
137
|
+
baseUrl = "https://api.example.com"
|
|
138
|
+
timeout = 15_000
|
|
139
|
+
interceptor { AuthInterceptor(tokenProvider) }
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## References
|
|
144
|
+
|
|
145
|
+
See skill: `kotlin-coroutines-flows` for detailed coroutine patterns.
|
|
146
|
+
See skill: `android-clean-architecture` for module and layer patterns.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.kt"
|
|
4
|
+
- "**/*.kts"
|
|
5
|
+
---
|
|
6
|
+
# Kotlin Security
|
|
7
|
+
|
|
8
|
+
> This file extends [common/security.md](../common/security.md) with Kotlin and Android/KMP-specific content.
|
|
9
|
+
|
|
10
|
+
## Secrets Management
|
|
11
|
+
|
|
12
|
+
- Never hardcode API keys, tokens, or credentials in source code
|
|
13
|
+
- Use `local.properties` (git-ignored) for local development secrets
|
|
14
|
+
- Use `BuildConfig` fields generated from CI secrets for release builds
|
|
15
|
+
- Use `EncryptedSharedPreferences` (Android) or Keychain (iOS) for runtime secret storage
|
|
16
|
+
|
|
17
|
+
```kotlin
|
|
18
|
+
// BAD
|
|
19
|
+
val apiKey = "sk-abc123..."
|
|
20
|
+
|
|
21
|
+
// GOOD — from BuildConfig (generated at build time)
|
|
22
|
+
val apiKey = BuildConfig.API_KEY
|
|
23
|
+
|
|
24
|
+
// GOOD — from secure storage at runtime
|
|
25
|
+
val token = secureStorage.get("auth_token")
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Network Security
|
|
29
|
+
|
|
30
|
+
- Use HTTPS exclusively — configure `network_security_config.xml` to block cleartext
|
|
31
|
+
- Pin certificates for sensitive endpoints using OkHttp `CertificatePinner` or Ktor equivalent
|
|
32
|
+
- Set timeouts on all HTTP clients — never leave defaults (which may be infinite)
|
|
33
|
+
- Validate and sanitize all server responses before use
|
|
34
|
+
|
|
35
|
+
```xml
|
|
36
|
+
<!-- res/xml/network_security_config.xml -->
|
|
37
|
+
<network-security-config>
|
|
38
|
+
<base-config cleartextTrafficPermitted="false" />
|
|
39
|
+
</network-security-config>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Input Validation
|
|
43
|
+
|
|
44
|
+
- Validate all user input before processing or sending to API
|
|
45
|
+
- Use parameterized queries for Room/SQLDelight — never concatenate user input into SQL
|
|
46
|
+
- Sanitize file paths from user input to prevent path traversal
|
|
47
|
+
|
|
48
|
+
```kotlin
|
|
49
|
+
// BAD — SQL injection
|
|
50
|
+
@Query("SELECT * FROM items WHERE name = '$input'")
|
|
51
|
+
|
|
52
|
+
// GOOD — parameterized
|
|
53
|
+
@Query("SELECT * FROM items WHERE name = :input")
|
|
54
|
+
fun findByName(input: String): List<ItemEntity>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Data Protection
|
|
58
|
+
|
|
59
|
+
- Use `EncryptedSharedPreferences` for sensitive key-value data on Android
|
|
60
|
+
- Use `@Serializable` with explicit field names — don't leak internal property names
|
|
61
|
+
- Clear sensitive data from memory when no longer needed
|
|
62
|
+
- Use `@Keep` or ProGuard rules for serialized classes to prevent name mangling
|
|
63
|
+
|
|
64
|
+
## Authentication
|
|
65
|
+
|
|
66
|
+
- Store tokens in secure storage, not in plain SharedPreferences
|
|
67
|
+
- Implement token refresh with proper 401/403 handling
|
|
68
|
+
- Clear all auth state on logout (tokens, cached user data, cookies)
|
|
69
|
+
- Use biometric authentication (`BiometricPrompt`) for sensitive operations
|
|
70
|
+
|
|
71
|
+
## ProGuard / R8
|
|
72
|
+
|
|
73
|
+
- Keep rules for all serialized models (`@Serializable`, Gson, Moshi)
|
|
74
|
+
- Keep rules for reflection-based libraries (Koin, Retrofit)
|
|
75
|
+
- Test release builds — obfuscation can break serialization silently
|
|
76
|
+
|
|
77
|
+
## WebView Security
|
|
78
|
+
|
|
79
|
+
- Disable JavaScript unless explicitly needed: `settings.javaScriptEnabled = false`
|
|
80
|
+
- Validate URLs before loading in WebView
|
|
81
|
+
- Never expose `@JavascriptInterface` methods that access sensitive data
|
|
82
|
+
- Use `WebViewClient.shouldOverrideUrlLoading()` to control navigation
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- "**/*.kt"
|
|
4
|
+
- "**/*.kts"
|
|
5
|
+
---
|
|
6
|
+
# Kotlin Testing
|
|
7
|
+
|
|
8
|
+
> This file extends [common/testing.md](../common/testing.md) with Kotlin and Android/KMP-specific content.
|
|
9
|
+
|
|
10
|
+
## Test Framework
|
|
11
|
+
|
|
12
|
+
- **kotlin.test** for multiplatform (KMP) — `@Test`, `assertEquals`, `assertTrue`
|
|
13
|
+
- **JUnit 4/5** for Android-specific tests
|
|
14
|
+
- **Turbine** for testing Flows and StateFlow
|
|
15
|
+
- **kotlinx-coroutines-test** for coroutine testing (`runTest`, `TestDispatcher`)
|
|
16
|
+
|
|
17
|
+
## ViewModel Testing with Turbine
|
|
18
|
+
|
|
19
|
+
```kotlin
|
|
20
|
+
@Test
|
|
21
|
+
fun `loading state emitted then data`() = runTest {
|
|
22
|
+
val repo = FakeItemRepository()
|
|
23
|
+
repo.addItem(testItem)
|
|
24
|
+
val viewModel = ItemListViewModel(GetItemsUseCase(repo))
|
|
25
|
+
|
|
26
|
+
viewModel.state.test {
|
|
27
|
+
assertEquals(ItemListState(), awaitItem()) // initial state
|
|
28
|
+
viewModel.onEvent(ItemListEvent.Load)
|
|
29
|
+
assertTrue(awaitItem().isLoading) // loading
|
|
30
|
+
assertEquals(listOf(testItem), awaitItem().items) // loaded
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Fakes Over Mocks
|
|
36
|
+
|
|
37
|
+
Prefer hand-written fakes over mocking frameworks:
|
|
38
|
+
|
|
39
|
+
```kotlin
|
|
40
|
+
class FakeItemRepository : ItemRepository {
|
|
41
|
+
private val items = mutableListOf<Item>()
|
|
42
|
+
var fetchError: Throwable? = null
|
|
43
|
+
|
|
44
|
+
override suspend fun getAll(): Result<List<Item>> {
|
|
45
|
+
fetchError?.let { return Result.failure(it) }
|
|
46
|
+
return Result.success(items.toList())
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
override fun observeAll(): Flow<List<Item>> = flowOf(items.toList())
|
|
50
|
+
|
|
51
|
+
fun addItem(item: Item) { items.add(item) }
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Coroutine Testing
|
|
56
|
+
|
|
57
|
+
```kotlin
|
|
58
|
+
@Test
|
|
59
|
+
fun `parallel operations complete`() = runTest {
|
|
60
|
+
val repo = FakeRepository()
|
|
61
|
+
val result = loadDashboard(repo)
|
|
62
|
+
advanceUntilIdle()
|
|
63
|
+
assertNotNull(result.items)
|
|
64
|
+
assertNotNull(result.stats)
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use `runTest` — it auto-advances virtual time and provides `TestScope`.
|
|
69
|
+
|
|
70
|
+
## Ktor MockEngine
|
|
71
|
+
|
|
72
|
+
```kotlin
|
|
73
|
+
val mockEngine = MockEngine { request ->
|
|
74
|
+
when (request.url.encodedPath) {
|
|
75
|
+
"/api/items" -> respond(
|
|
76
|
+
content = Json.encodeToString(testItems),
|
|
77
|
+
headers = headersOf(HttpHeaders.ContentType, ContentType.Application.Json.toString())
|
|
78
|
+
)
|
|
79
|
+
else -> respondError(HttpStatusCode.NotFound)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
val client = HttpClient(mockEngine) {
|
|
84
|
+
install(ContentNegotiation) { json() }
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Room/SQLDelight Testing
|
|
89
|
+
|
|
90
|
+
- Room: Use `Room.inMemoryDatabaseBuilder()` for in-memory testing
|
|
91
|
+
- SQLDelight: Use `JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)` for JVM tests
|
|
92
|
+
|
|
93
|
+
```kotlin
|
|
94
|
+
@Test
|
|
95
|
+
fun `insert and query items`() = runTest {
|
|
96
|
+
val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY)
|
|
97
|
+
Database.Schema.create(driver)
|
|
98
|
+
val db = Database(driver)
|
|
99
|
+
|
|
100
|
+
db.itemQueries.insert("1", "Sample Item", "description")
|
|
101
|
+
val items = db.itemQueries.getAll().executeAsList()
|
|
102
|
+
assertEquals(1, items.size)
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Test Naming
|
|
107
|
+
|
|
108
|
+
Use backtick-quoted descriptive names:
|
|
109
|
+
|
|
110
|
+
```kotlin
|
|
111
|
+
@Test
|
|
112
|
+
fun `search with empty query returns all items`() = runTest { }
|
|
113
|
+
|
|
114
|
+
@Test
|
|
115
|
+
fun `delete item emits updated list without deleted item`() = runTest { }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Test Organization
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
src/
|
|
122
|
+
├── commonTest/kotlin/ # Shared tests (ViewModel, UseCase, Repository)
|
|
123
|
+
├── androidUnitTest/kotlin/ # Android unit tests (JUnit)
|
|
124
|
+
├── androidInstrumentedTest/kotlin/ # Instrumented tests (Room, UI)
|
|
125
|
+
└── iosTest/kotlin/ # iOS-specific tests
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Minimum test coverage: ViewModel + UseCase for every feature.
|