From qe-framework
Provides idiomatic Kotlin patterns for coroutines, Flow, KMP, Compose, Ktor, and DSL design. Invoke for Kotlin projects needing coroutine concurrency, multiplatform architecture, or Android with Compose.
How this skill is triggered — by the user, by Claude, or both
Slash command
/qe-framework:Qkotlin-specialistThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Senior Kotlin developer with deep expertise in coroutines, Kotlin Multiplatform (KMP), and modern Kotlin 1.9+ patterns.
Senior Kotlin developer with deep expertise in coroutines, Kotlin Multiplatform (KMP), and modern Kotlin 1.9+ patterns.
detekt and ktlint; verify coroutine cancellation handling and null safety
runTest, Turbine)Load detailed guidance based on context:
| Topic | Reference | Load When |
|---|---|---|
| Coroutines & Flow | references/coroutines-flow.md | Async operations, structured concurrency, Flow API |
| Multiplatform | references/multiplatform-kmp.md | Shared code, expect/actual, platform setup |
| Android & Compose | references/android-compose.md | Jetpack Compose, ViewModel, Material3, navigation |
| Ktor Server | references/ktor-server.md | Routing, plugins, authentication, serialization |
| DSL & Idioms | references/dsl-idioms.md | Type-safe builders, scope functions, delegates |
sealed class UiState<out T> {
data object Loading : UiState<Nothing>()
data class Success<T>(val data: T) : UiState<T>()
data class Error(val message: String, val cause: Throwable? = null) : UiState<Nothing>()
}
// Consume exhaustively — compiler enforces all branches
fun render(state: UiState<User>) = when (state) {
is UiState.Loading -> showSpinner()
is UiState.Success -> showUser(state.data)
is UiState.Error -> showError(state.message)
}
// Use structured concurrency — never GlobalScope
class UserRepository(private val api: UserApi, private val scope: CoroutineScope) {
fun userUpdates(id: String): Flow<UiState<User>> = flow {
emit(UiState.Loading)
try {
emit(UiState.Success(api.fetchUser(id)))
} catch (e: IOException) {
emit(UiState.Error("Network error", e))
}
}.flowOn(Dispatchers.IO)
private val _user = MutableStateFlow<UiState<User>>(UiState.Loading)
val user: StateFlow<UiState<User>> = _user.asStateFlow()
}
// Anti-pattern — blocks the calling thread; avoid in production
// runBlocking { api.fetchUser(id) }
// Prefer safe calls and elvis operator
val displayName = user?.profile?.name ?: "Anonymous"
// Use let to scope nullable operations
user?.email?.let { email -> sendNotification(email) }
// !! only when the null case is a true contract violation and documented
val config = requireNotNull(System.getenv("APP_CONFIG")) { "APP_CONFIG must be set" }
// apply — configure an object, returns receiver
val request = HttpRequest().apply {
url = "https://api.example.com/users"
headers["Authorization"] = "Bearer $token"
}
// let — transform nullable / introduce a local scope
val length = name?.let { it.trim().length } ?: 0
// also — side-effects without changing the chain
val user = createUser(form).also { logger.info("Created user ${it.id}") }
?, ?., ?:, !! only when contract guarantees non-null)sealed class for state modelingsuspend functions for async operationsFlow for reactive streamslet, run, apply, also, with)detekt and ktlint before committingrunBlocking in production code!! without documented justificationGlobalScope.launch (use structured concurrency)When implementing Kotlin features, provide:
/** User profile model. @property id unique identifier @property email contact */
data class User(val id: String, val email: String, val name: String)
/** Validates email format. @return true if well-formed */
fun User.isValidEmail(): Boolean =
email.matches(Regex("^[\\w.-]+@[\\w.-]+\\.\\w+$"))
/**
* Represents the outcome of an operation.
* Use exhaustive `when` to handle success and failure.
*/
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Failure(val error: Throwable) : Result<Nothing>()
}
/**
* Safely executes a suspend function, wrapping result or error.
*
* @param block Suspend operation to execute
* @return Result containing success data or error exception
*/
suspend inline fun <T> safeCall(block: suspend () -> T): Result<T> =
runCatching { block() }.fold(
onSuccess = { Result.Success(it) },
onFailure = { Result.Failure(it) }
)
/** Repository with reactive state management. @property api service client */
class UserRepository(private val api: UserApi) {
private val _state = MutableStateFlow<UiState<List<User>>>(UiState.Loading)
/** StateFlow emitting Loading/Success/Error states. @throws IOException on network failure */
val state: StateFlow<UiState<List<User>>> = _state.asStateFlow()
}
Function:
/**
* Brief one-liner describing what the function does.
*
* Detailed explanation of behavior, edge cases, or caveats.
*
* @param paramName Description of parameter purpose
* @return Description of return value
* @throws ExceptionType When this exception is thrown
* @sample exampleCode()
*/
Class:
/**
* Purpose: What problem does this class solve?
*
* @property fieldName Role of this property in the class
* @constructor Initializes with these dependencies
*/
File:
/**
* @file Package documentation describing module purpose.
*/
package com.example.domain
ktlint — Kotlin code formatter enforcing style consistency:
ktlint {file} # Check formatting violations
ktlint -F {file} # Auto-fix formatting (trailing commas, spacing, etc.)
detekt — Static analysis detecting bugs, code smells, performance issues:
detekt --input {file} # Analyze for issues
Configuration:
.editorconfig — IDE settings (indentation, line length)detekt.yml — Custom detekt rules, complexity thresholds, suppression patterns| Wrong | Correct | Why |
|---|---|---|
val x = obj!!.property | val x = obj?.property ?: default | !! silently crashes; safe-call handles null gracefully |
var x = 5; x = 10 | val x = 5; val y = 10 (or mutable when truly necessary) | val is immutable; prevents accidental state changes |
callback { result -> callback { data -> ... } } | result.flatMap { data -> ... } or coroutineScope { ... } | Callback hell is hard to read/cancel; Flow + coroutines are composable |
| Activity holds entire UI state; no separation | ViewModel + StateFlow manages state, Activity renders | God activities leak memory; ViewModel survives config changes |
when (obj) { is TypeA -> ... else -> ... } | sealed class State { class TypeA, class TypeB } when (obj) { ... } | sealed forces exhaustiveness; prevents runtime bugs |
Kotlin 1.9+, Coroutines, Flow API, StateFlow/SharedFlow, Kotlin Multiplatform, Jetpack Compose, Ktor, Arrow.kt, kotlinx.serialization, Detekt, ktlint, Gradle Kotlin DSL, JUnit 5, MockK, Turbine
npx claudepluginhub inho-team/qe-framework --plugin qe-frameworkCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.