Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Cursor rules for Kotlin development with Ktor integration.
.claude/skills/amariahak-cursor-rules-for-kotlin-development-with-ktor-integration/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 63% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 53% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 63% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 105% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 89% | 0% |
Cursor rules for Kotlin development with Ktor integration.
Synced from https://github.com/PatrickJS/awesome-cursorrules/tree/main/rules/kotlin-ktor-development-cursorrules-prompt-file.mdc.
src/main/kotlin/com/company/app/
├── common/ # Shared utilities, extensions
├── config/ # Application configuration, DI
└── features/
├── auth/ # Feature directory
│ ├── models/
│ ├── repositories/
│ ├── services/
│ └── routes/
└── users/ # Another feature
├── ...Test structure mirrors the feature-based organization:
src/test/kotlin/com/company/app/
├── common/
└── features/
├── auth/
│ ├── models/
│ ├── repositories/
│ ├── services/
│ └── routes/
└── users/
├── ...kotlininterface UserRepository { suspend fun findById(id: UUID): UserDTO? suspend fun create(user: CreateUserRequest): UserDTO suspend fun update(id: UUID, user: UpdateUserRequest): UserDTO? suspend fun delete(id: UUID): Boolean } class UserRepositoryImpl : UserRepository { override suspend fun findById(id: UUID): UserDTO? = withContext(Dispatchers.IO) { transaction { Users.select { Users.id eq id } .mapNotNull { it.toUserDTO() } .singleOrNull() } } // Other implementations... }
kotlininterface UserService { suspend fun getUserById(id: UUID): UserDTO suspend fun createUser(request: CreateUserRequest): UserDTO suspend fun updateUser(id: UUID, request: UpdateUserRequest): UserDTO suspend fun deleteUser(id: UUID) } class UserServiceImpl( private val userRepository: UserRepository ) : UserService { override suspend fun getUserById(id: UUID): UserDTO { return userRepository.findById(id) ?: throw ResourceNotFoundException("User", id.toString()) } // Other implementations... }
kotlinfun Application.configureUserRoutes(userService: UserService) { routing { route("/api/users") { get("/{id}") { val id = call.parameters["id"]?.let { UUID.fromString(it) } ?: throw ValidationException("Invalid ID format") val user = userService.getUserById(id) call.respond(ApiResponse("SUCCESS", "User retrieved", user)) } // Other routes... } } }
kotlinopen class ApplicationException( message: String, val statusCode: HttpStatusCode = HttpStatusCode.InternalServerError ) : RuntimeException(message) class ResourceNotFoundException(resource: String, id: String) : ApplicationException("$resource with ID $id not found", HttpStatusCode.NotFound) fun Application.configureExceptions() { install(StatusPages) { exception<ResourceNotFoundException> { call, cause -> call.respond(cause.statusCode, ApiResponse("ERROR", cause.message ?: "Resource not found")) } exception<Throwable> { call, cause -> call.respond(HttpStatusCode.InternalServerError, ApiResponse("ERROR", "An internal error occurred")) } } }
kotlinclass UserServiceTest : DescribeSpec({ describe("UserService") { val mockRepository = mockk<UserRepository>() val userService = UserServiceImpl(mockRepository) it("should return user when exists") { val userId = UUID.randomUUID() val user = UserDTO(userId.toString(), "Test User", "test@example.com") coEvery { mockRepository.findById(userId) } returns user val result = runBlocking { userService.getUserById(userId) } result shouldBe user } it("should throw exception when user not found") { val userId = UUID.randomUUID() coEvery { mockRepository.findById(userId) } returns null shouldThrow<ResourceNotFoundException> { runBlocking { userService.getUserById(userId) } } } } })
kotlinclass UserRoutesTest : FunSpec({ test("GET /api/users/{id} returns 200 when user exists") { val mockService = mockk<UserService>() val userId = UUID.randomUUID() val user = UserDTO(userId.toString(), "Test User", "test@example.com") coEvery { mockService.getUserById(userId) } returns user testApplication { application { configureRouting() configureDI { single { mockService } } } client.get("/api/users/$userId").apply { status shouldBe HttpStatusCode.OK bodyAsText().let { Json.decodeFromString<ApiResponse<UserDTO>>(it) }.data shouldBe user } } } })
kotlin// Type-safe configuration interface AppConfig { val database: DatabaseConfig val security: SecurityConfig } data class DatabaseConfig( val driver: String, val url: String, val user: String, val password: String ) // Access in application fun Application.configureDI() { val appConfig = HoconAppConfig(environment.config) install(Koin) { modules(module { single<AppConfig> { appConfig } single { appConfig.database } }) } }
kotlinfun Application.configureSecurity() { install(Authentication) { jwt("auth-jwt") { // JWT configuration } } install(DefaultHeaders) { header(HttpHeaders.XContentTypeOptions, "nosniff") header(HttpHeaders.XFrameOptions, "DENY") header(HttpHeaders.ContentSecurityPolicy, "default-src 'self'") header("Strict-Transport-Security", "max-age=31536000; includeSubDomains") } }
kotlinfun Application.configureMonitoring() { val startTime = System.currentTimeMillis() routing { get("/health") { call.respond(mapOf("status" to "UP", "uptime" to "${(System.currentTimeMillis() - startTime) / 1000}s")) } get("/metrics") { call.respond(prometheusRegistry.scrape()) } } install(MicrometerMetrics) { registry = PrometheusMeterRegistry(PrometheusConfig.DEFAULT) meterBinders = listOf( JvmMemoryMetrics(), JvmGcMetrics(), ProcessorMetrics(), JvmThreadMetrics() ) } }
-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:MaxRAMPercentage=75.0| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-04 | pass→pass | 14,542 | 12,903 | -11% | 1 | 1 | 0% | 3,101 | 5,172 | +67% | 0 | 0 | — |
case-01 | fail→pass | 17,361 | 24,347 | +40% | 1 | 1 | 0% | 4,141 | 6,736 | +63% | 0 | 0 | — |
case-02 | fail→pass | 16,419 | 14,746 | -10% | 1 | 1 | 0% | 3,674 | 5,624 | +53% | 0 | 0 | — |
case-03 | fail→pass | 14,528 | 11,545 | -21% | 1 | 1 | 0% | 3,039 | 4,963 | +63% | 0 | 0 | — |
case-05 | pass→pass | 15,270 | 15,475 | +1% | 1 | 1 | 0% | 3,021 | 5,478 | +81% | 0 | 0 | — |
case-06 | pass→pass | 17,107 | 16,777 | -2% | 1 | 1 | 0% | 3,863 | 6,064 | +57% | 0 | 0 | — |
case-07 | pass→pass | 15,735 | 8,480 | -46% | 1 | 1 | 0% | 3,302 | 3,959 | +20% | 0 | 0 | — |
case-08 | pass→pass | 11,545 | 8,888 | -23% | 1 | 1 | 0% | 2,397 | 3,969 | +66% | 0 | 0 | — |
case-09 | fail→fail | 14,133 | 13,490 | -5% | 1 | 1 | 0% | 3,082 | 5,217 | +69% | 0 | 0 | — |
case-10 | pass→pass | 11,366 | 10,751 | -5% | 1 | 1 | 0% | 2,395 | 4,500 | +88% | 0 | 0 | — |
case-11 | fail→pass | 9,950 | 9,185 | -8% | 1 | 1 | 0% | 2,161 | 4,430 | +105% | 0 | 0 | — |
case-12 | pass→pass | 17,115 | 11,791 | -31% | 1 | 1 | 0% | 2,934 | 4,383 | +49% | 0 | 0 | — |
case-13 | fail→pass | 10,895 | 9,635 | -12% | 1 | 1 | 0% | 2,295 | 4,335 | +89% | 0 | 0 | — |
case-14 | pass→pass | 14,165 | 11,473 | -19% | 1 | 1 | 0% | 2,732 | 4,753 | +74% | 0 | 0 | — |
case-15 | pass→pass | 8,832 | 6,846 | -22% | 1 | 1 | 0% | 1,945 | 3,698 | +90% | 0 | 0 | — |
case-16 | pass→pass | 8,374 | 7,528 | -10% | 1 | 1 | 0% | 1,749 | 3,860 | +121% | 0 | 0 | — |
case-17 | fail→pass | 13,359 | 7,697 | -42% | 1 | 1 | 0% | 2,662 | 3,961 | +49% | 0 | 0 | — |
case-18 | fail→pass | 9,784 | 6,966 | -29% | 1 | 1 | 0% | 1,746 | 3,630 | +108% | 0 | 0 | — |
case-19 | fail→pass | 12,887 | 10,274 | -20% | 1 | 1 | 0% | 2,276 | 3,842 | +69% | 0 | 0 | — |
case-20 | pass→pass | 10,176 | 8,357 | -18% | 1 | 1 | 0% | 1,907 | 3,907 | +105% | 0 | 0 | — |
case-21 | fail→pass | 20,657 | 9,915 | -52% | 1 | 1 | 0% | 3,493 | 4,172 | +19% | 0 | 0 | — |
case-22 | pass→pass | 12,881 | 8,431 | -35% | 1 | 1 | 0% | 2,679 | 4,125 | +54% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +41 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.