ai-toolkit-rules
Mandatory engineering, security, testing, git, performance, quality, and response rules.…
Kotlin: coroutines, Flow, sealed/data classes, null safety, Ktor, Compose, KMP. Triggers: Kotlin, coroutine, Flow, suspend, Ktor, Jetpack Compose, KMP, kotlinx.
$ npx -y skills add softspark/ai-toolkit --skill kotlin-patterns --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/kotlin-patternsContext preview
The summary Claude sees to decide when to auto-load this skill.
Kotlin: coroutines, Flow, sealed/data classes, null safety, Ktor, Compose, KMP. Triggers: Kotlin, coroutine, Flow, suspend, Ktor, Jetpack Compose, KMP, kotlinx.
name: kotlin-patterns description: "Kotlin: coroutines, Flow, sealed/data classes, null safety, Ktor, Compose, KMP. Triggers: Kotlin, coroutine, Flow, suspend, Ktor, Jetpack Compose, KMP, kotlinx." effort: medium user-invocable: false allowed-tools: Read
project-root/
├── build.gradle.kts
├── settings.gradle.kts
├── gradle/libs.versions.toml
├── app/
│ ├── build.gradle.kts
│ └── src/{main,test}/kotlin/com/example/app/
├── domain/
│ └── src/main/kotlin/com/example/domain/
│ ├── model/
│ ├── repository/
│ └── usecase/
└── infrastructure/
└── src/main/kotlin/com/example/infra/rootProject.name = "my-project"
dependencyResolutionManagement {
versionCatalogs { create("libs") { from(files("gradle/libs.versions.toml")) } }
}
include(":app", ":domain", ":infrastructure")plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.kotlin.serialization)
}
dependencies {
implementation(project(":domain"))
implementation(libs.kotlinx.coroutines.core)
testImplementation(libs.bundles.testing)
}
kotlin { jvmToolchain(21) }---
data class User(
val id: UserId,
val name: String,
val email: String,
val role: Role = Role.USER,
) {
init {
require(name.isNotBlank()) { "Name must not be blank" }
require(email.contains("@")) { "Invalid email format" }
}
}
@JvmInline
value class UserId(val value: String) // Zero-overhead type-safe ID
enum class Role { ADMIN, USER, GUEST }sealed interface PaymentResult {
data class Success(val transactionId: String, val amount: Money) : PaymentResult
data class Declined(val reason: String) : PaymentResult
data class Error(val exception: Throwable) : PaymentResult
}
// Exhaustive when -- compiler enforces all branches
fun handlePayment(result: PaymentResult): String = when (result) {
is PaymentResult.Success -> "Paid: ${result.amount}"
is PaymentResult.Declined -> "Declined: ${result.reason}"
is PaymentResult.Error -> "Error: ${result.exception.message}"
}fun String.toSlug(): String =
lowercase().replace(Regex("[^a-z0-9\\s-]"), "").replace(Regex("\\s+"), "-").trim('-')
// Scoped extensions -- visible only inside containing class
class OrderService {
private fun Order.totalWithTax(): Money = total * (1 + taxRate)
}fun getDisplayName(user: User?): String =
user?.name?.takeIf { it.isNotBlank() } ?: "Anonymous"
fun processEmail(email: String?) { email?.let { sendWelcomeEmail(it) } }
fun loadConfig(path: String?): Config {
val resolved = requireNotNull(path) { "Config path must not be null" }
return parseConfig(resolved)
}| Function | Ref | Returns | Use case | |----------|-----|---------|----------| | `let` | `it` | Lambda result | Null check + transform | | `run` | `this` | Lambda result | Config + compute | | `apply` | `this` | Object itself | Object initialization | | `also` | `it` | Object itself | Side effects |
val conn = Connection().apply { host = "localhost"; port = 5432 }
fun createUser(req: CreateUserRequest): User =
userRepository.save(req.toUser()).also { logger.info("Created: ${it.id}") }fun html(block: HtmlBuilder.() -> Unit): String = HtmlBuilder().apply(block).build()
val page = html {
head { title("My Page") }
body { p("Hello, world!") }
}---
fun findUser(id: UserId): Result<User> = runCatching {
userRepository.findById(id) ?: throw UserNotFoundException(id)
}
fun getUserDisplayName(id: UserId): String =
findUser(id).map { it.name }.recover { "Unknown User" }.getOrThrow()
fun handleLookup(id: UserId): Response = findUser(id).fold(
onSuccess = { Response.ok(it) },
onFailure = { Response.notFound(it.message) },
)sealed class DomainError(override val message: String) : Exception(message) {
data class NotFound(val resource: String, val id: String) : DomainError("$resource not found: $id")
data class Validation(val field: String, val reason: String) : DomainError("Invalid $field: $reason")
data class Conflict(val detail: String) : DomainError("Conflict: $detail")
}
fun handleError(error: DomainError): Response = when (error) {
is DomainError.NotFound -> Response.status(404).body(error.message)
is DomainError.Validation -> Response.status(422).body(error.message)
is DomainError.Conflict -> Response.status(409).body(error.message)
}fun transferMoney(from: Account, to: Account, amount: Money) {
require(amount.value > 0) { "Transfer amount must be positive" }
require(from.id != to.id) { "Cannot transfer to same account" }
check(from.balance >= amount) { "Insufficient funds: ${from.balance}" }
}---
class UserServiceTest {
private val repository = mockk<UserRepository>()
private val notifier = mockk<NotificationService>(relaxed = true)
private val service = UserService(repository, notifier)
@Test
fun `creates user and sends welcome notification`() {
val expected = User(UserId("1"), "Alice", "alice@test.com")
every { repository.save(any()) } returns expected
val result = service.createUser(CreateUserRequest("Alice", "alice@test.com"))
assertThat(result).isEqualTo(expected)
verify(exactly = 1) { notifier.sendWelcome(expected) }
}
@Test
fun `throws on duplicate email`() {
every { repository.save(any()) } throws DomainError.Conflict("EAI coding toolkit with machine-enforced safety, 116 skills, 44 agents, lifecycle hooks, persona presets, opt-in plugin packs, and benchmark tooling.
Repo: softspark/ai-toolkit
Mandatory engineering, security, testing, git, performance, quality, and response rules.…
Searches past coding sessions for observations, decisions, context. Triggers: mem-search,…
Accessibility validator: WCAG 2.1 AA, EN 301 549, EAA. Triggers: a11y, accessibility, WCAG,…
Creates new specialized agents with frontmatter, tools, delegation. Triggers: new agent,…
Analyzes code quality, complexity, patterns across codebase. Triggers: quality report,…
API design: naming, versioning, pagination, idempotency, OpenAPI, error contracts and safe…