$npx -y skills add softspark/ai-toolkit --skill kotlin-rulesKotlin coding rules: style, patterns, security, testing. Triggers: .kt, .kts, build.gradle.kts, Ktor, Jetpack Compose, coroutines, kotlinx.
| 1 | # Kotlin Rules |
| 2 | |
| 3 | These rules come from `app/rules/kotlin/` in ai-toolkit. They cover |
| 4 | the project's standards for coding style, frameworks, patterns, |
| 5 | security, and testing in Kotlin. Apply them when writing or |
| 6 | reviewing Kotlin code. |
| 7 | |
| 8 | # Kotlin Coding Style |
| 9 | |
| 10 | ## Naming |
| 11 | - PascalCase: classes, interfaces, objects, type aliases, enum entries. |
| 12 | - camelCase: functions, properties, local variables, parameters. |
| 13 | - UPPER_SNAKE: compile-time constants (`const val`), top-level `val` constants. |
| 14 | - Backing properties: prefix with `_` (`private val _items`, `val items: List<T>`). |
| 15 | - Package names: lowercase, no underscores (`com.company.project.feature`). |
| 16 | |
| 17 | ## Null Safety |
| 18 | - Use nullable types only when nullability is semantically meaningful. |
| 19 | - Prefer `?.let { }`, `?:` (Elvis), and safe calls over `!!`. |
| 20 | - Never use `!!` except in tests or when null is truly impossible. |
| 21 | - Use `requireNotNull()` and `require()` for preconditions at public API boundaries. |
| 22 | - Use `checkNotNull()` and `check()` for state assertions. |
| 23 | |
| 24 | ## Data Classes |
| 25 | - Use `data class` for DTOs, value objects, and state containers. |
| 26 | - Use `copy()` for immutable updates. Avoid mutable `var` in data classes. |
| 27 | - Use `sealed class` / `sealed interface` for restricted hierarchies. |
| 28 | - Use `value class` (inline class) for type-safe wrappers with zero overhead. |
| 29 | - Use `object` for singletons and namespace-like utility groupings. |
| 30 | |
| 31 | ## Functions |
| 32 | - Use expression body (`= expr`) for single-expression functions. |
| 33 | - Use named arguments for functions with >2 parameters of the same type. |
| 34 | - Use default parameter values instead of overloaded functions. |
| 35 | - Use extension functions to add behavior without inheritance. |
| 36 | - Use `suspend` functions for async operations, not callbacks. |
| 37 | |
| 38 | ## Collections |
| 39 | - Prefer `listOf`, `mapOf`, `setOf` (immutable) over `mutableListOf`. |
| 40 | - Use collection operators: `map`, `filter`, `groupBy`, `associate`. |
| 41 | - Use `sequence {}` for lazy evaluation on large collections. |
| 42 | - Prefer `firstOrNull()` over `first()` for safe access. |
| 43 | - Use destructuring: `val (name, age) = user`. |
| 44 | |
| 45 | ## Scope Functions |
| 46 | - `let`: null-safe chaining and local scoping. |
| 47 | - `apply`: configure object after creation. |
| 48 | - `also`: side effects (logging, validation) in chains. |
| 49 | - `run`: compute a result using receiver's context. |
| 50 | - `with`: multiple operations on an object without chaining. |
| 51 | - Avoid nesting scope functions more than 1 level deep. |
| 52 | |
| 53 | ## Formatting |
| 54 | - Use ktlint or detekt for automated formatting and linting. |
| 55 | - Use trailing commas in multi-line parameter/argument lists. |
| 56 | - Max line length: 120 characters (Kotlin convention). |
| 57 | - Use `when` expression over if-else chains for 3+ branches. |
| 58 | |
| 59 | # Kotlin Frameworks |
| 60 | |
| 61 | ## Ktor (Server) |
| 62 | - Use routing DSL: `routing { get("/users") { call.respond(users) } }`. |
| 63 | - Use `install()` for plugins: ContentNegotiation, Authentication, CORS. |
| 64 | - Use `call.receive<T>()` for typed request body parsing with kotlinx.serialization. |
| 65 | - Use `StatusPages` plugin for centralized error handling. |
| 66 | - Use `Routing` with nested `route("/api/v1") { }` blocks for URL grouping. |
| 67 | |
| 68 | ## Ktor (Client) |
| 69 | - Use `HttpClient` with engine configuration (CIO, OkHttp, Apache). |
| 70 | - Use `install(ContentNegotiation) { json() }` for JSON serialization. |
| 71 | - Use `client.get<T>()` with reified type for typed responses. |
| 72 | - Use `HttpTimeout` plugin for connection and request timeouts. |
| 73 | - Close `HttpClient` when done or use DI lifecycle management. |
| 74 | |
| 75 | ## Spring Boot (Kotlin) |
| 76 | - Use constructor injection (Kotlin classes are `final` by default). |
| 77 | - Apply `kotlin-spring` plugin for open classes (required for proxying). |
| 78 | - Use `@ConfigurationProperties` with data classes for typed config. |
| 79 | - Use `WebFlux` with coroutines: `coRouter { }` and `suspend` handler functions. |
| 80 | - Use `spring-boot-starter-validation` with `@Valid` on Kotlin data classes. |
| 81 | |
| 82 | ## Exposed (ORM) |
| 83 | - Use DSL API for type-safe queries: `Users.select { Users.name eq "Ada" }`. |
| 84 | - Use DAO API for Active Record-style: `User.find { Users.age greaterEq 18 }`. |
| 85 | - Wrap database operations in `transaction { }` blocks. |
| 86 | - Use `SchemaUtils.create(Users)` for schema management in development. |
| 87 | |
| 88 | ## kotlinx.serialization |
| 89 | - Use `@Serializable` annotation on data classes for compile-time serialization. |
| 90 | - Use `@SerialName("field_name")` for JSON field name mapping. |
| 91 | - Use `Json { ignoreUnknownKeys = true }` for lenient deserialization. |
| 92 | - Use polymorphic serialization with `sealed class` and `@Polymorphic`. |
| 93 | - Prefer `kotlinx.serialization` over Jackson for pure Kotlin projects. |
| 94 | |
| 95 | ## Koin (DI) |
| 96 | - Define modules: `module { single { UserService(get()) } }`. |
| 97 | - Use `by inject<T>()` for lazy injection in Android/Ktor. |
| 98 | - Use `factory { }` for new instance per injection, `single { }` for singleton. |
| 99 | - Use `checkModules()` in tests to verify DI graph completeness. |
| 100 | |
| 101 | ## Compose (Multi |