[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6JV4RR40DAQDEPMPACHJSXW":3,"skill-same:01M6JV4RR40DAQDEPMPACHJSXW":67},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":37,"linkOnly":38,"markdownContent":39,"markdownBody":40,"frontMatter":41,"refName":43,"commitSha":44,"skillDirectory":45,"forksCount":46,"repositoryHtmlUrl":47,"repositoryHomepageUrl":48,"ownerHtmlUrl":49,"ownerBlogUrl":50,"ownerDescription":51,"updatedAt":52,"install":53,"resources":66},"01M6JV4RR40DAQDEPMPACHJSXW","kotlin-ktor-patterns",null,"Ktor server patterns including routing DSL, plugins, authentication, Koin DI, kotlinx.serialization, WebSockets, and testApplication testing.","hashgraph-online\u002Fawesome-codex-plugins","hashgraph-online","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F160371962?v=4","01M6CNB6NRJVNNGN14TR01Q6D3","HOL (Hashgraph Online)","01M6JT1HSKKG9V68KQAQGBA7XP",1559,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002FColin4k1024\u002Ftsp\u002Fskills\u002Fkotlin-ktor-patterns\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36],"kotlin ktor patterns","kotlin","patterns","ktor","testing","routing","authentication","plugins","serialization","server","websockets","dsl","di","2026-11-10 10:12:30",false,"---\nname: kotlin-ktor-patterns\ndescription: Ktor server patterns including routing DSL, plugins, authentication, Koin DI, kotlinx.serialization, WebSockets, and testApplication testing.\norigin: ECC\n---\n\n# Ktor Server Patterns\n\nComprehensive Ktor patterns for building robust, maintainable HTTP servers with Kotlin coroutines.\n\n## When to Activate\n\n- Building Ktor HTTP servers\n- Configuring Ktor plugins (Auth, CORS, ContentNegotiation, StatusPages)\n- Implementing REST APIs with Ktor\n- Setting up dependency injection with Koin\n- Writing Ktor integration tests with testApplication\n- Working with WebSockets in Ktor\n\n## Application Structure\n\n### Standard Ktor Project Layout\n\n```text\nsrc\u002Fmain\u002Fkotlin\u002F\n├── com\u002Fexample\u002F\n│   ├── Application.kt           # Entry point, module configuration\n│   ├── plugins\u002F\n│   │   ├── Routing.kt           # Route definitions\n│   │   ├── Serialization.kt     # Content negotiation setup\n│   │   ├── Authentication.kt    # Auth configuration\n│   │   ├── StatusPages.kt       # Error handling\n│   │   └── CORS.kt              # CORS configuration\n│   ├── routes\u002F\n│   │   ├── UserRoutes.kt        # \u002Fusers endpoints\n│   │   ├── AuthRoutes.kt        # \u002Fauth endpoints\n│   │   └── HealthRoutes.kt      # \u002Fhealth endpoints\n│   ├── models\u002F\n│   │   ├── User.kt              # Domain models\n│   │   └── ApiResponse.kt       # Response envelopes\n│   ├── services\u002F\n│   │   ├── UserService.kt       # Business logic\n│   │   └── AuthService.kt       # Auth logic\n│   ├── repositories\u002F\n│   │   ├── UserRepository.kt    # Data access interface\n│   │   └── ExposedUserRepository.kt\n│   └── di\u002F\n│       └── AppModule.kt         # Koin modules\nsrc\u002Ftest\u002Fkotlin\u002F\n├── com\u002Fexample\u002F\n│   ├── routes\u002F\n│   │   └── UserRoutesTest.kt\n│   └── services\u002F\n│       └── UserServiceTest.kt\n```\n\n### Application Entry Point\n\n```kotlin\n\u002F\u002F Application.kt\nfun main() {\n    embeddedServer(Netty, port = 8080, module = Application::module).start(wait = true)\n}\n\nfun Application.module() {\n    configureSerialization()\n    configureAuthentication()\n    configureStatusPages()\n    configureCORS()\n    configureDI()\n    configureRouting()\n}\n```\n\n## Routing DSL\n\n### Basic Routes\n\n```kotlin\n\u002F\u002F plugins\u002FRouting.kt\nfun Application.configureRouting() {\n    routing {\n        userRoutes()\n        authRoutes()\n        healthRoutes()\n    }\n}\n\n\u002F\u002F routes\u002FUserRoutes.kt\nfun Route.userRoutes() {\n    val userService by inject\u003CUserService>()\n\n    route(\"\u002Fusers\") {\n        get {\n            val users = userService.getAll()\n            call.respond(users)\n        }\n\n        get(\"\u002F{id}\") {\n            val id = call.parameters[\"id\"]\n                ?: return@get call.respond(HttpStatusCode.BadRequest, \"Missing id\")\n            val user = userService.getById(id)\n                ?: return@get call.respond(HttpStatusCode.NotFound)\n            call.respond(user)\n        }\n\n        post {\n            val request = call.receive\u003CCreateUserRequest>()\n            val user = userService.create(request)\n            call.respond(HttpStatusCode.Created, user)\n        }\n\n        put(\"\u002F{id}\") {\n            val id = call.parameters[\"id\"]\n                ?: return@put call.respond(HttpStatusCode.BadRequest, \"Missing id\")\n            val request = call.receive\u003CUpdateUserRequest>()\n            val user = userService.update(id, request)\n                ?: return@put call.respond(HttpStatusCode.NotFound)\n            call.respond(user)\n        }\n\n        delete(\"\u002F{id}\") {\n            val id = call.parameters[\"id\"]\n                ?: return@delete call.respond(HttpStatusCode.BadRequest, \"Missing id\")\n            val deleted = userService.delete(id)\n            if (deleted) call.respond(HttpStatusCode.NoContent)\n            else call.respond(HttpStatusCode.NotFound)\n        }\n    }\n}\n```\n\n### Route Organization with Authenticated Routes\n\n```kotlin\nfun Route.userRoutes() {\n    route(\"\u002Fusers\") {\n        \u002F\u002F Public routes\n        get { \u002F* list users *\u002F }\n        get(\"\u002F{id}\") { \u002F* get user *\u002F }\n\n        \u002F\u002F Protected routes\n        authenticate(\"jwt\") {\n            post { \u002F* create user - requires auth *\u002F }\n            put(\"\u002F{id}\") { \u002F* update user - requires auth *\u002F }\n            delete(\"\u002F{id}\") { \u002F* delete user - requires auth *\u002F }\n        }\n    }\n}\n```\n\n## Content Negotiation & Serialization\n\n### kotlinx.serialization Setup\n\n```kotlin\n\u002F\u002F plugins\u002FSerialization.kt\nfun Application.configureSerialization() {\n    install(ContentNegotiation) {\n        json(Json {\n            prettyPrint = true\n            isLenient = false\n            ignoreUnknownKeys = true\n            encodeDefaults = true\n            explicitNulls = false\n        })\n    }\n}\n```\n\n### Serializable Models\n\n```kotlin\n@Serializable\ndata class UserResponse(\n    val id: String,\n    val name: String,\n    val email: String,\n    val role: Role,\n    @Serializable(with = InstantSerializer::class)\n    val createdAt: Instant,\n)\n\n@Serializable\ndata class CreateUserRequest(\n    val name: String,\n    val email: String,\n    val role: Role = Role.USER,\n)\n\n@Serializable\ndata class ApiResponse\u003CT>(\n    val success: Boolean,\n    val data: T? = null,\n    val error: String? = null,\n) {\n    companion object {\n        fun \u003CT> ok(data: T): ApiResponse\u003CT> = ApiResponse(success = true, data = data)\n        fun \u003CT> error(message: String): ApiResponse\u003CT> = ApiResponse(success = false, error = message)\n    }\n}\n\n@Serializable\ndata class PaginatedResponse\u003CT>(\n    val data: List\u003CT>,\n    val total: Long,\n    val page: Int,\n    val limit: Int,\n)\n```\n\n### Custom Serializers\n\n```kotlin\nobject InstantSerializer : KSerializer\u003CInstant> {\n    override val descriptor = PrimitiveSerialDescriptor(\"Instant\", PrimitiveKind.STRING)\n    override fun serialize(encoder: Encoder, value: Instant) =\n        encoder.encodeString(value.toString())\n    override fun deserialize(decoder: Decoder): Instant =\n        Instant.parse(decoder.decodeString())\n}\n```\n\n## Authentication\n\n### JWT Authentication\n\n```kotlin\n\u002F\u002F plugins\u002FAuthentication.kt\nfun Application.configureAuthentication() {\n    val jwtSecret = environment.config.property(\"jwt.secret\").getString()\n    val jwtIssuer = environment.config.property(\"jwt.issuer\").getString()\n    val jwtAudience = environment.config.property(\"jwt.audience\").getString()\n    val jwtRealm = environment.config.property(\"jwt.realm\").getString()\n\n    install(Authentication) {\n        jwt(\"jwt\") {\n            realm = jwtRealm\n            verifier(\n                JWT.require(Algorithm.HMAC256(jwtSecret))\n                    .withAudience(jwtAudience)\n                    .withIssuer(jwtIssuer)\n                    .build()\n            )\n            validate { credential ->\n                if (credential.payload.audience.contains(jwtAudience)) {\n                    JWTPrincipal(credential.payload)\n                } else {\n                    null\n                }\n            }\n            challenge { _, _ ->\n                call.respond(HttpStatusCode.Unauthorized, ApiResponse.error\u003CUnit>(\"Invalid or expired token\"))\n            }\n        }\n    }\n}\n\n\u002F\u002F Extracting user from JWT\nfun ApplicationCall.userId(): String =\n    principal\u003CJWTPrincipal>()\n        ?.payload\n        ?.getClaim(\"userId\")\n        ?.asString()\n        ?: throw AuthenticationException(\"No userId in token\")\n```\n\n### Auth Routes\n\n```kotlin\nfun Route.authRoutes() {\n    val authService by inject\u003CAuthService>()\n\n    route(\"\u002Fauth\") {\n        post(\"\u002Flogin\") {\n            val request = call.receive\u003CLoginRequest>()\n            val token = authService.login(request.email, request.password)\n                ?: return@post call.respond(\n                    HttpStatusCode.Unauthorized,\n                    ApiResponse.error\u003CUnit>(\"Invalid credentials\"),\n                )\n            call.respond(ApiResponse.ok(TokenResponse(token)))\n        }\n\n        post(\"\u002Fregister\") {\n            val request = call.receive\u003CRegisterRequest>()\n            val user = authService.register(request)\n            call.respond(HttpStatusCode.Created, ApiResponse.ok(user))\n        }\n\n        authenticate(\"jwt\") {\n            get(\"\u002Fme\") {\n                val userId = call.userId()\n                val user = authService.getProfile(userId)\n                call.respond(ApiResponse.ok(user))\n            }\n        }\n    }\n}\n```\n\n## Status Pages (Error Handling)\n\n```kotlin\n\u002F\u002F plugins\u002FStatusPages.kt\nfun Application.configureStatusPages() {\n    install(StatusPages) {\n        exception\u003CContentTransformationException> { call, cause ->\n            call.respond(\n                HttpStatusCode.BadRequest,\n                ApiResponse.error\u003CUnit>(\"Invalid request body: ${cause.message}\"),\n            )\n        }\n\n        exception\u003CIllegalArgumentException> { call, cause ->\n            call.respond(\n                HttpStatusCode.BadRequest,\n                ApiResponse.error\u003CUnit>(cause.message ?: \"Bad request\"),\n            )\n        }\n\n        exception\u003CAuthenticationException> { call, _ ->\n            call.respond(\n                HttpStatusCode.Unauthorized,\n                ApiResponse.error\u003CUnit>(\"Authentication required\"),\n            )\n        }\n\n        exception\u003CAuthorizationException> { call, _ ->\n            call.respond(\n                HttpStatusCode.Forbidden,\n                ApiResponse.error\u003CUnit>(\"Access denied\"),\n            )\n        }\n\n        exception\u003CNotFoundException> { call, cause ->\n            call.respond(\n                HttpStatusCode.NotFound,\n                ApiResponse.error\u003CUnit>(cause.message ?: \"Resource not found\"),\n            )\n        }\n\n        exception\u003CThrowable> { call, cause ->\n            call.application.log.error(\"Unhandled exception\", cause)\n            call.respond(\n                HttpStatusCode.InternalServerError,\n                ApiResponse.error\u003CUnit>(\"Internal server error\"),\n            )\n        }\n\n        status(HttpStatusCode.NotFound) { call, status ->\n            call.respond(status, ApiResponse.error\u003CUnit>(\"Route not found\"))\n        }\n    }\n}\n```\n\n## CORS Configuration\n\n```kotlin\n\u002F\u002F plugins\u002FCORS.kt\nfun Application.configureCORS() {\n    install(CORS) {\n        allowHost(\"localhost:3000\")\n        allowHost(\"example.com\", schemes = listOf(\"https\"))\n        allowHeader(HttpHeaders.ContentType)\n        allowHeader(HttpHeaders.Authorization)\n        allowMethod(HttpMethod.Put)\n        allowMethod(HttpMethod.Delete)\n        allowMethod(HttpMethod.Patch)\n        allowCredentials = true\n        maxAgeInSeconds = 3600\n    }\n}\n```\n\n## Koin Dependency Injection\n\n### Module Definition\n\n```kotlin\n\u002F\u002F di\u002FAppModule.kt\nval appModule = module {\n    \u002F\u002F Database\n    single\u003CDatabase> { DatabaseFactory.create(get()) }\n\n    \u002F\u002F Repositories\n    single\u003CUserRepository> { ExposedUserRepository(get()) }\n    single\u003COrderRepository> { ExposedOrderRepository(get()) }\n\n    \u002F\u002F Services\n    single { UserService(get()) }\n    single { OrderService(get(), get()) }\n    single { AuthService(get(), get()) }\n}\n\n\u002F\u002F Application setup\nfun Application.configureDI() {\n    install(Koin) {\n        modules(appModule)\n    }\n}\n```\n\n### Using Koin in Routes\n\n```kotlin\nfun Route.userRoutes() {\n    val userService by inject\u003CUserService>()\n\n    route(\"\u002Fusers\") {\n        get {\n            val users = userService.getAll()\n            call.respond(ApiResponse.ok(users))\n        }\n    }\n}\n```\n\n### Koin for Testing\n\n```kotlin\nclass UserServiceTest : FunSpec(), KoinTest {\n    override fun extensions() = listOf(KoinExtension(testModule))\n\n    private val testModule = module {\n        single\u003CUserRepository> { mockk() }\n        single { UserService(get()) }\n    }\n\n    private val repository by inject\u003CUserRepository>()\n    private val service by inject\u003CUserService>()\n\n    init {\n        test(\"getUser returns user\") {\n            coEvery { repository.findById(\"1\") } returns testUser\n            service.getById(\"1\") shouldBe testUser\n        }\n    }\n}\n```\n\n## Request Validation\n\n```kotlin\n\u002F\u002F Validate request data in routes\nfun Route.userRoutes() {\n    val userService by inject\u003CUserService>()\n\n    post(\"\u002Fusers\") {\n        val request = call.receive\u003CCreateUserRequest>()\n\n        \u002F\u002F Validate\n        require(request.name.isNotBlank()) { \"Name is required\" }\n        require(request.name.length \u003C= 100) { \"Name must be 100 characters or less\" }\n        require(request.email.matches(Regex(\".+@.+\\\\..+\"))) { \"Invalid email format\" }\n\n        val user = userService.create(request)\n        call.respond(HttpStatusCode.Created, ApiResponse.ok(user))\n    }\n}\n\n\u002F\u002F Or use a validation extension\nfun CreateUserRequest.validate() {\n    require(name.isNotBlank()) { \"Name is required\" }\n    require(name.length \u003C= 100) { \"Name must be 100 characters or less\" }\n    require(email.matches(Regex(\".+@.+\\\\..+\"))) { \"Invalid email format\" }\n}\n```\n\n## WebSockets\n\n```kotlin\nfun Application.configureWebSockets() {\n    install(WebSockets) {\n        pingPeriod = 15.seconds\n        timeout = 15.seconds\n        maxFrameSize = 64 * 1024 \u002F\u002F 64 KiB — increase only if your protocol requires larger frames\n        masking = false \u002F\u002F Server-to-client frames are unmasked per RFC 6455; client-to-server are always masked by Ktor\n    }\n}\n\nfun Route.chatRoutes() {\n    val connections = Collections.synchronizedSet\u003CConnection>(LinkedHashSet())\n\n    webSocket(\"\u002Fchat\") {\n        val thisConnection = Connection(this)\n        connections += thisConnection\n\n        try {\n            send(\"Connected! Users online: ${connections.size}\")\n\n            for (frame in incoming) {\n                frame as? Frame.Text ?: continue\n                val text = frame.readText()\n                val message = ChatMessage(thisConnection.name, text)\n\n                \u002F\u002F Snapshot under lock to avoid ConcurrentModificationException\n                val snapshot = synchronized(connections) { connections.toList() }\n                snapshot.forEach { conn ->\n                    conn.session.send(Json.encodeToString(message))\n                }\n            }\n        } catch (e: Exception) {\n            logger.error(\"WebSocket error\", e)\n        } finally {\n            connections -= thisConnection\n        }\n    }\n}\n\ndata class Connection(val session: DefaultWebSocketSession) {\n    val name: String = \"User-${counter.getAndIncrement()}\"\n\n    companion object {\n        private val counter = AtomicInteger(0)\n    }\n}\n```\n\n## testApplication Testing\n\n### Basic Route Testing\n\n```kotlin\nclass UserRoutesTest : FunSpec({\n    test(\"GET \u002Fusers returns list of users\") {\n        testApplication {\n            application {\n                install(Koin) { modules(testModule) }\n                configureSerialization()\n                configureRouting()\n            }\n\n            val response = client.get(\"\u002Fusers\")\n\n            response.status shouldBe HttpStatusCode.OK\n            val body = response.body\u003CApiResponse\u003CList\u003CUserResponse>>>()\n            body.success shouldBe true\n            body.data.shouldNotBeNull().shouldNotBeEmpty()\n        }\n    }\n\n    test(\"POST \u002Fusers creates a user\") {\n        testApplication {\n            application {\n                install(Koin) { modules(testModule) }\n                configureSerialization()\n                configureStatusPages()\n                configureRouting()\n            }\n\n            val client = createClient {\n                install(io.ktor.client.plugins.contentnegotiation.ContentNegotiation) {\n                    json()\n                }\n            }\n\n            val response = client.post(\"\u002Fusers\") {\n                contentType(ContentType.Application.Json)\n                setBody(CreateUserRequest(\"Alice\", \"alice@example.com\"))\n            }\n\n            response.status shouldBe HttpStatusCode.Created\n        }\n    }\n\n    test(\"GET \u002Fusers\u002F{id} returns 404 for unknown id\") {\n        testApplication {\n            application {\n                install(Koin) { modules(testModule) }\n                configureSerialization()\n                configureStatusPages()\n                configureRouting()\n            }\n\n            val response = client.get(\"\u002Fusers\u002Funknown-id\")\n\n            response.status shouldBe HttpStatusCode.NotFound\n        }\n    }\n})\n```\n\n### Testing Authenticated Routes\n\n```kotlin\nclass AuthenticatedRoutesTest : FunSpec({\n    test(\"protected route requires JWT\") {\n        testApplication {\n            application {\n                install(Koin) { modules(testModule) }\n                configureSerialization()\n                configureAuthentication()\n                configureRouting()\n            }\n\n            val response = client.post(\"\u002Fusers\") {\n                contentType(ContentType.Application.Json)\n                setBody(CreateUserRequest(\"Alice\", \"alice@example.com\"))\n            }\n\n            response.status shouldBe HttpStatusCode.Unauthorized\n        }\n    }\n\n    test(\"protected route succeeds with valid JWT\") {\n        testApplication {\n            application {\n                install(Koin) { modules(testModule) }\n                configureSerialization()\n                configureAuthentication()\n                configureRouting()\n            }\n\n            val token = generateTestJWT(userId = \"test-user\")\n\n            val client = createClient {\n                install(io.ktor.client.plugins.contentnegotiation.ContentNegotiation) { json() }\n            }\n\n            val response = client.post(\"\u002Fusers\") {\n                contentType(ContentType.Application.Json)\n                bearerAuth(token)\n                setBody(CreateUserRequest(\"Alice\", \"alice@example.com\"))\n            }\n\n            response.status shouldBe HttpStatusCode.Created\n        }\n    }\n})\n```\n\n## Configuration\n\n### application.yaml\n\n```yaml\nktor:\n  application:\n    modules:\n      - com.example.ApplicationKt.module\n  deployment:\n    port: 8080\n\njwt:\n  secret: ${JWT_SECRET}\n  issuer: \"https:\u002F\u002Fexample.com\"\n  audience: \"https:\u002F\u002Fexample.com\u002Fapi\"\n  realm: \"example\"\n\ndatabase:\n  url: ${DATABASE_URL}\n  driver: \"org.postgresql.Driver\"\n  maxPoolSize: 10\n```\n\n### Reading Config\n\n```kotlin\nfun Application.configureDI() {\n    val dbUrl = environment.config.property(\"database.url\").getString()\n    val dbDriver = environment.config.property(\"database.driver\").getString()\n    val maxPoolSize = environment.config.property(\"database.maxPoolSize\").getString().toInt()\n\n    install(Koin) {\n        modules(module {\n            single { DatabaseConfig(dbUrl, dbDriver, maxPoolSize) }\n            single { DatabaseFactory.create(get()) }\n        })\n    }\n}\n```\n\n## Quick Reference: Ktor Patterns\n\n| Pattern | Description |\n|---------|-------------|\n| `route(\"\u002Fpath\") { get { } }` | Route grouping with DSL |\n| `call.receive\u003CT>()` | Deserialize request body |\n| `call.respond(status, body)` | Send response with status |\n| `call.parameters[\"id\"]` | Read path parameters |\n| `call.request.queryParameters[\"q\"]` | Read query parameters |\n| `install(Plugin) { }` | Install and configure plugin |\n| `authenticate(\"name\") { }` | Protect routes with auth |\n| `by inject\u003CT>()` | Koin dependency injection |\n| `testApplication { }` | Integration testing |\n\n**Remember**: Ktor is designed around Kotlin coroutines and DSLs. Keep routes thin, push logic to services, and use Koin for dependency injection. Test with `testApplication` for full integration coverage.\n","# Ktor Server Patterns\n\nComprehensive Ktor patterns for building robust, maintainable HTTP servers with Kotlin coroutines.\n\n## When to Activate\n\n- Building Ktor HTTP servers\n- Configuring Ktor plugins (Auth, CORS, ContentNegotiation, StatusPages)\n- Implementing REST APIs with Ktor\n- Setting up dependency injection with Koin\n- Writing Ktor integration tests with testApplication\n- Working with WebSockets in Ktor\n\n## Application Structure\n\n### Standard Ktor Project Layout\n\n```text\nsrc\u002Fmain\u002Fkotlin\u002F\n├── com\u002Fexample\u002F\n│   ├── Application.kt           # Entry point, module configuration\n│   ├── plugins\u002F\n│   │   ├── Routing.kt           # Route definitions\n│   │   ├── Serialization.kt     # Content negotiation setup\n│   │   ├── Authentication.kt    # Auth configuration\n│   │   ├── StatusPages.kt       # Error handling\n│   │   └── CORS.kt              # CORS configuration\n│   ├── routes\u002F\n│   │   ├── UserRoutes.kt        # \u002Fusers endpoints\n│   │   ├── AuthRoutes.kt        # \u002Fauth endpoints\n│   │   └── HealthRoutes.kt      # \u002Fhealth endpoints\n│   ├── models\u002F\n│   │   ├── User.kt              # Domain models\n│   │   └── ApiResponse.kt       # Response envelopes\n│   ├── services\u002F\n│   │   ├── UserService.kt       # Business logic\n│   │   └── AuthService.kt       # Auth logic\n│   ├── repositories\u002F\n│   │   ├── UserRepository.kt    # Data access interface\n│   │   └── ExposedUserRepository.kt\n│   └── di\u002F\n│       └── AppModule.kt         # Koin modules\nsrc\u002Ftest\u002Fkotlin\u002F\n├── com\u002Fexample\u002F\n│   ├── routes\u002F\n│   │   └── UserRoutesTest.kt\n│   └── services\u002F\n│       └── UserServiceTest.kt\n```\n\n### Application Entry Point\n\n```kotlin\n\u002F\u002F Application.kt\nfun main() {\n    embeddedServer(Netty, port = 8080, module = Application::module).start(wait = true)\n}\n\nfun Application.module() {\n    configureSerialization()\n    configureAuthentication()\n    configureStatusPages()\n    configureCORS()\n    configureDI()\n    configureRouting()\n}\n```\n\n## Routing DSL\n\n### Basic Routes\n\n```kotlin\n\u002F\u002F plugins\u002FRouting.kt\nfun Application.configureRouting() {\n    routing {\n        userRoutes()\n        authRoutes()\n        healthRoutes()\n    }\n}\n\n\u002F\u002F routes\u002FUserRoutes.kt\nfun Route.userRoutes() {\n    val userService by inject\u003CUserService>()\n\n    route(\"\u002Fusers\") {\n        get {\n            val users = userService.getAll()\n            call.respond(users)\n        }\n\n        get(\"\u002F{id}\") {\n            val id = call.parameters[\"id\"]\n                ?: return@get call.respond(HttpStatusCode.BadRequest, \"Missing id\")\n            val user = userService.getById(id)\n                ?: return@get call.respond(HttpStatusCode.NotFound)\n            call.respond(user)\n        }\n\n        post {\n            val request = call.receive\u003CCreateUserRequest>()\n            val user = userService.create(request)\n            call.respond(HttpStatusCode.Created, user)\n        }\n\n        put(\"\u002F{id}\") {\n            val id = call.parameters[\"id\"]\n                ?: return@put call.respond(HttpStatusCode.BadRequest, \"Missing id\")\n            val request = call.receive\u003CUpdateUserRequest>()\n            val user = userService.update(id, request)\n                ?: return@put call.respond(HttpStatusCode.NotFound)\n            call.respond(user)\n        }\n\n        delete(\"\u002F{id}\") {\n            val id = call.parameters[\"id\"]\n                ?: return@delete call.respond(HttpStatusCode.BadRequest, \"Missing id\")\n            val deleted = userService.delete(id)\n            if (deleted) call.respond(HttpStatusCode.NoContent)\n            else call.respond(HttpStatusCode.NotFound)\n        }\n    }\n}\n```\n\n### Route Organization with Authenticated Routes\n\n```kotlin\nfun Route.userRoutes() {\n    route(\"\u002Fusers\") {\n        \u002F\u002F Public routes\n        get { \u002F* list users *\u002F }\n        get(\"\u002F{id}\") { \u002F* get user *\u002F }\n\n        \u002F\u002F Protected routes\n        authenticate(\"jwt\") {\n            post { \u002F* create user - requires auth *\u002F }\n            put(\"\u002F{id}\") { \u002F* update user - requires auth *\u002F }\n            delete(\"\u002F{id}\") { \u002F* delete user - requires auth *\u002F }\n        }\n    }\n}\n```\n\n## Content Negotiation & Serialization\n\n### kotlinx.serialization Setup\n\n```kotlin\n\u002F\u002F plugins\u002FSerialization.kt\nfun Application.configureSerialization() {\n    install(ContentNegotiation) {\n        json(Json {\n            prettyPrint = true\n            isLenient = false\n            ignoreUnknownKeys = true\n            encodeDefaults = true\n            explicitNulls = false\n        })\n    }\n}\n```\n\n### Serializable Models\n\n```kotlin\n@Serializable\ndata class UserResponse(\n    val id: String,\n    val name: String,\n    val email: String,\n    val role: Role,\n    @Serializable(with = InstantSerializer::class)\n    val createdAt: Instant,\n)\n\n@Serializable\ndata class CreateUserRequest(\n    val name: String,\n    val email: String,\n    val role: Role = Role.USER,\n)\n\n@Serializable\ndata class ApiResponse\u003CT>(\n    val success: Boolean,\n    val data: T? = null,\n    val error: String? = null,\n) {\n    companion object {\n        fun \u003CT> ok(data: T): ApiResponse\u003CT> = ApiResponse(success = true, data = data)\n        fun \u003CT> error(message: String): ApiResponse\u003CT> = ApiResponse(success = false, error = message)\n    }\n}\n\n@Serializable\ndata class PaginatedResponse\u003CT>(\n    val data: List\u003CT>,\n    val total: Long,\n    val page: Int,\n    val limit: Int,\n)\n```\n\n### Custom Serializers\n\n```kotlin\nobject InstantSerializer : KSerializer\u003CInstant> {\n    override val descriptor = PrimitiveSerialDescriptor(\"Instant\", PrimitiveKind.STRING)\n    override fun serialize(encoder: Encoder, value: Instant) =\n        encoder.encodeString(value.toString())\n    override fun deserialize(decoder: Decoder): Instant =\n        Instant.parse(decoder.decodeString())\n}\n```\n\n## Authentication\n\n### JWT Authentication\n\n```kotlin\n\u002F\u002F plugins\u002FAuthentication.kt\nfun Application.configureAuthentication() {\n    val jwtSecret = environment.config.property(\"jwt.secret\").getString()\n    val jwtIssuer = environment.config.property(\"jwt.issuer\").getString()\n    val jwtAudience = environment.config.property(\"jwt.audience\").getString()\n    val jwtRealm = environment.config.property(\"jwt.realm\").getString()\n\n    install(Authentication) {\n        jwt(\"jwt\") {\n            realm = jwtRealm\n            verifier(\n                JWT.require(Algorithm.HMAC256(jwtSecret))\n                    .withAudience(jwtAudience)\n                    .withIssuer(jwtIssuer)\n                    .build()\n            )\n            validate { credential ->\n                if (credential.payload.audience.contains(jwtAudience)) {\n                    JWTPrincipal(credential.payload)\n                } else {\n                    null\n                }\n            }\n            challenge { _, _ ->\n                call.respond(HttpStatusCode.Unauthorized, ApiResponse.error\u003CUnit>(\"Invalid or expired token\"))\n            }\n        }\n    }\n}\n\n\u002F\u002F Extracting user from JWT\nfun ApplicationCall.userId(): String =\n    principal\u003CJWTPrincipal>()\n        ?.payload\n        ?.getClaim(\"userId\")\n        ?.asString()\n        ?: throw AuthenticationException(\"No userId in token\")\n```\n\n### Auth Routes\n\n```kotlin\nfun Route.authRoutes() {\n    val authService by inject\u003CAuthService>()\n\n    route(\"\u002Fauth\") {\n        post(\"\u002Flogin\") {\n            val request = call.receive\u003CLoginRequest>()\n            val token = authService.login(request.email, request.password)\n                ?: return@post call.respond(\n                    HttpStatusCode.Unauthorized,\n                    ApiResponse.error\u003CUnit>(\"Invalid credentials\"),\n                )\n            call.respond(ApiResponse.ok(TokenResponse(token)))\n        }\n\n        post(\"\u002Fregister\") {\n            val request = call.receive\u003CRegisterRequest>()\n            val user = authService.register(request)\n            call.respond(HttpStatusCode.Created, ApiResponse.ok(user))\n        }\n\n        authenticate(\"jwt\") {\n            get(\"\u002Fme\") {\n                val userId = call.userId()\n                val user = authService.getProfile(userId)\n                call.respond(ApiResponse.ok(user))\n            }\n        }\n    }\n}\n```\n\n## Status Pages (Error Handling)\n\n```kotlin\n\u002F\u002F plugins\u002FStatusPages.kt\nfun Application.configureStatusPages() {\n    install(StatusPages) {\n        exception\u003CContentTransformationException> { call, cause ->\n            call.respond(\n                HttpStatusCode.BadRequest,\n                ApiResponse.error\u003CUnit>(\"Invalid request body: ${cause.message}\"),\n            )\n        }\n\n        exception\u003CIllegalArgumentException> { call, cause ->\n            call.respond(\n                HttpStatusCode.BadRequest,\n                ApiResponse.error\u003CUnit>(cause.message ?: \"Bad request\"),\n            )\n        }\n\n        exception\u003CAuthenticationException> { call, _ ->\n            call.respond(\n                HttpStatusCode.Unauthorized,\n                ApiResponse.error\u003CUnit>(\"Authentication required\"),\n            )\n        }\n\n        exception\u003CAuthorizationException> { call, _ ->\n            call.respond(\n                HttpStatusCode.Forbidden,\n                ApiResponse.error\u003CUnit>(\"Access denied\"),\n            )\n        }\n\n        exception\u003CNotFoundException> { call, cause ->\n            call.respond(\n                HttpStatusCode.NotFound,\n                ApiResponse.error\u003CUnit>(cause.message ?: \"Resource not found\"),\n            )\n        }\n\n        exception\u003CThrowable> { call, cause ->\n            call.application.log.error(\"Unhandled exception\", cause)\n            call.respond(\n                HttpStatusCode.InternalServerError,\n                ApiResponse.error\u003CUnit>(\"Internal server error\"),\n            )\n        }\n\n        status(HttpStatusCode.NotFound) { call, status ->\n            call.respond(status, ApiResponse.error\u003CUnit>(\"Route not found\"))\n        }\n    }\n}\n```\n\n## CORS Configuration\n\n```kotlin\n\u002F\u002F plugins\u002FCORS.kt\nfun Application.configureCORS() {\n    install(CORS) {\n        allowHost(\"localhost:3000\")\n        allowHost(\"example.com\", schemes = listOf(\"https\"))\n        allowHeader(HttpHeaders.ContentType)\n        allowHeader(HttpHeaders.Authorization)\n        allowMethod(HttpMethod.Put)\n        allowMethod(HttpMethod.Delete)\n        allowMethod(HttpMethod.Patch)\n        allowCredentials = true\n        maxAgeInSeconds = 3600\n    }\n}\n```\n\n## Koin Dependency Injection\n\n### Module Definition\n\n```kotlin\n\u002F\u002F di\u002FAppModule.kt\nval appModule = module {\n    \u002F\u002F Database\n    single\u003CDatabase> { DatabaseFactory.create(get()) }\n\n    \u002F\u002F Repositories\n    single\u003CUserRepository> { ExposedUserRepository(get()) }\n    single\u003COrderRepository> { ExposedOrderRepository(get()) }\n\n    \u002F\u002F Services\n    single { UserService(get()) }\n    single { OrderService(get(), get()) }\n    single { AuthService(get(), get()) }\n}\n\n\u002F\u002F Application setup\nfun Application.configureDI() {\n    install(Koin) {\n        modules(appModule)\n    }\n}\n```\n\n### Using Koin in Routes\n\n```kotlin\nfun Route.userRoutes() {\n    val userService by inject\u003CUserService>()\n\n    route(\"\u002Fusers\") {\n        get {\n            val users = userService.getAll()\n            call.respond(ApiResponse.ok(users))\n        }\n    }\n}\n```\n\n### Koin for Testing\n\n```kotlin\nclass UserServiceTest : FunSpec(), KoinTest {\n    override fun extensions() = listOf(KoinExtension(testModule))\n\n    private val testModule = module {\n        single\u003CUserRepository> { mockk() }\n        single { UserService(get()) }\n    }\n\n    private val repository by inject\u003CUserRepository>()\n    private val service by inject\u003CUserService>()\n\n    init {\n        test(\"getUser returns user\") {\n            coEvery { repository.findById(\"1\") } returns testUser\n            service.getById(\"1\") shouldBe testUser\n        }\n    }\n}\n```\n\n## Request Validation\n\n```kotlin\n\u002F\u002F Validate request data in routes\nfun Route.userRoutes() {\n    val userService by inject\u003CUserService>()\n\n    post(\"\u002Fusers\") {\n        val request = call.receive\u003CCreateUserRequest>()\n\n        \u002F\u002F Validate\n        require(request.name.isNotBlank()) { \"Name is required\" }\n        require(request.name.length \u003C= 100) { \"Name must be 100 characters or less\" }\n        require(request.email.matches(Regex(\".+@.+\\\\..+\"))) { \"Invalid email format\" }\n\n        val user = userService.create(request)\n        call.respond(HttpStatusCode.Created, ApiResponse.ok(user))\n    }\n}\n\n\u002F\u002F Or use a validation extension\nfun CreateUserRequest.validate() {\n    require(name.isNotBlank()) { \"Name is required\" }\n    require(name.length \u003C= 100) { \"Name must be 100 characters or less\" }\n    require(email.matches(Regex(\".+@.+\\\\..+\"))) { \"Invalid email format\" }\n}\n```\n\n## WebSockets\n\n```kotlin\nfun Application.configureWebSockets() {\n    install(WebSockets) {\n        pingPeriod = 15.seconds\n        timeout = 15.seconds\n        maxFrameSize = 64 * 1024 \u002F\u002F 64 KiB — increase only if your protocol requires larger frames\n        masking = false \u002F\u002F Server-to-client frames are unmasked per RFC 6455; client-to-server are always masked by Ktor\n    }\n}\n\nfun Route.chatRoutes() {\n    val connections = Collections.synchronizedSet\u003CConnection>(LinkedHashSet())\n\n    webSocket(\"\u002Fchat\") {\n        val thisConnection = Connection(this)\n        connections += thisConnection\n\n        try {\n            send(\"Connected! Users online: ${connections.size}\")\n\n            for (frame in incoming) {\n                frame as? Frame.Text ?: continue\n                val text = frame.readText()\n                val message = ChatMessage(thisConnection.name, text)\n\n                \u002F\u002F Snapshot under lock to avoid ConcurrentModificationException\n                val snapshot = synchronized(connections) { connections.toList() }\n                snapshot.forEach { conn ->\n                    conn.session.send(Json.encodeToString(message))\n                }\n            }\n        } catch (e: Exception) {\n            logger.error(\"WebSocket error\", e)\n        } finally {\n            connections -= thisConnection\n        }\n    }\n}\n\ndata class Connection(val session: DefaultWebSocketSession) {\n    val name: String = \"User-${counter.getAndIncrement()}\"\n\n    companion object {\n        private val counter = AtomicInteger(0)\n    }\n}\n```\n\n## testApplication Testing\n\n### Basic Route Testing\n\n```kotlin\nclass UserRoutesTest : FunSpec({\n    test(\"GET \u002Fusers returns list of users\") {\n        testApplication {\n            application {\n                install(Koin) { modules(testModule) }\n                configureSerialization()\n                configureRouting()\n            }\n\n            val response = client.get(\"\u002Fusers\")\n\n            response.status shouldBe HttpStatusCode.OK\n            val body = response.body\u003CApiResponse\u003CList\u003CUserResponse>>>()\n            body.success shouldBe true\n            body.data.shouldNotBeNull().shouldNotBeEmpty()\n        }\n    }\n\n    test(\"POST \u002Fusers creates a user\") {\n        testApplication {\n            application {\n                install(Koin) { modules(testModule) }\n                configureSerialization()\n                configureStatusPages()\n                configureRouting()\n            }\n\n            val client = createClient {\n                install(io.ktor.client.plugins.contentnegotiation.ContentNegotiation) {\n                    json()\n                }\n            }\n\n            val response = client.post(\"\u002Fusers\") {\n                contentType(ContentType.Application.Json)\n                setBody(CreateUserRequest(\"Alice\", \"alice@example.com\"))\n            }\n\n            response.status shouldBe HttpStatusCode.Created\n        }\n    }\n\n    test(\"GET \u002Fusers\u002F{id} returns 404 for unknown id\") {\n        testApplication {\n            application {\n                install(Koin) { modules(testModule) }\n                configureSerialization()\n                configureStatusPages()\n                configureRouting()\n            }\n\n            val response = client.get(\"\u002Fusers\u002Funknown-id\")\n\n            response.status shouldBe HttpStatusCode.NotFound\n        }\n    }\n})\n```\n\n### Testing Authenticated Routes\n\n```kotlin\nclass AuthenticatedRoutesTest : FunSpec({\n    test(\"protected route requires JWT\") {\n        testApplication {\n            application {\n                install(Koin) { modules(testModule) }\n                configureSerialization()\n                configureAuthentication()\n                configureRouting()\n            }\n\n            val response = client.post(\"\u002Fusers\") {\n                contentType(ContentType.Application.Json)\n                setBody(CreateUserRequest(\"Alice\", \"alice@example.com\"))\n            }\n\n            response.status shouldBe HttpStatusCode.Unauthorized\n        }\n    }\n\n    test(\"protected route succeeds with valid JWT\") {\n        testApplication {\n            application {\n                install(Koin) { modules(testModule) }\n                configureSerialization()\n                configureAuthentication()\n                configureRouting()\n            }\n\n            val token = generateTestJWT(userId = \"test-user\")\n\n            val client = createClient {\n                install(io.ktor.client.plugins.contentnegotiation.ContentNegotiation) { json() }\n            }\n\n            val response = client.post(\"\u002Fusers\") {\n                contentType(ContentType.Application.Json)\n                bearerAuth(token)\n                setBody(CreateUserRequest(\"Alice\", \"alice@example.com\"))\n            }\n\n            response.status shouldBe HttpStatusCode.Created\n        }\n    }\n})\n```\n\n## Configuration\n\n### application.yaml\n\n```yaml\nktor:\n  application:\n    modules:\n      - com.example.ApplicationKt.module\n  deployment:\n    port: 8080\n\njwt:\n  secret: ${JWT_SECRET}\n  issuer: \"https:\u002F\u002Fexample.com\"\n  audience: \"https:\u002F\u002Fexample.com\u002Fapi\"\n  realm: \"example\"\n\ndatabase:\n  url: ${DATABASE_URL}\n  driver: \"org.postgresql.Driver\"\n  maxPoolSize: 10\n```\n\n### Reading Config\n\n```kotlin\nfun Application.configureDI() {\n    val dbUrl = environment.config.property(\"database.url\").getString()\n    val dbDriver = environment.config.property(\"database.driver\").getString()\n    val maxPoolSize = environment.config.property(\"database.maxPoolSize\").getString().toInt()\n\n    install(Koin) {\n        modules(module {\n            single { DatabaseConfig(dbUrl, dbDriver, maxPoolSize) }\n            single { DatabaseFactory.create(get()) }\n        })\n    }\n}\n```\n\n## Quick Reference: Ktor Patterns\n\n| Pattern | Description |\n|---------|-------------|\n| `route(\"\u002Fpath\") { get { } }` | Route grouping with DSL |\n| `call.receive\u003CT>()` | Deserialize request body |\n| `call.respond(status, body)` | Send response with status |\n| `call.parameters[\"id\"]` | Read path parameters |\n| `call.request.queryParameters[\"q\"]` | Read query parameters |\n| `install(Plugin) { }` | Install and configure plugin |\n| `authenticate(\"name\") { }` | Protect routes with auth |\n| `by inject\u003CT>()` | Koin dependency injection |\n| `testApplication { }` | Integration testing |\n\n**Remember**: Ktor is designed around Kotlin coroutines and DSLs. Keep routes thin, push logic to services, and use Koin for dependency injection. Test with `testApplication` for full integration coverage.\n",{"name":5,"origin":42,"description":7},"ECC","main","b0f0a87593c578a7898eccaf1b363f7f87c1f99b","plugins\u002FColin4k1024\u002Ftsp\u002Fskills\u002Fkotlin-ktor-patterns",474,"https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins","https:\u002F\u002Fhol.org\u002Fplugins\u002Fbest-codex-plugins","https:\u002F\u002Fgithub.com\u002Fhashgraph-online","https:\u002F\u002Fhol.org","Open-source standards and developer tools for interoperable AI agents: discovery, identity, communication, payments, privacy and security.","2026-11-11 14:40:14",{"source":8,"skillName":5,"commands":54,"prompt":64,"zipAvailable":65},[55,58,61],{"tool":56,"command":57},"npx","npx skills add hashgraph-online\u002Fawesome-codex-plugins --skill kotlin-ktor-patterns",{"tool":59,"command":60},"bunx","bunx skills add hashgraph-online\u002Fawesome-codex-plugins --skill kotlin-ktor-patterns",{"tool":62,"command":63},"pnpm","pnpm dlx skills add hashgraph-online\u002Fawesome-codex-plugins --skill kotlin-ktor-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002FColin4k1024\u002Ftsp\u002Fskills\u002Fkotlin-ktor-patterns\u002FSKILL.md",true,[],[68,76,83,90,117,142],{"skillNo":69,"name":70,"nameZh":6,"description":71,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":72,"categories":73,"keywords":74,"publishedAt":75},"01M746ZWACVKMZ1BJXJZHYJSSX","migrate-to-e2e","Moves an existing Playwright, Cypress, Selenium, Appium, Detox, Maestro, or TesterArmy suite to e2e one slice at a time, with the old suite running beside the new one until each ported test is proven. Keeps a MIGRATION.md progress file so any later session resumes where the last stopped. Optionally plants a defect to show both suites catch it. Use for \u002Fmigrate-to-e2e, \"migrate our tests to e2e\", \"port these Maestro flows\", \"move our TesterArmy tests\", or when a project has an e2e config and an older suite still running.","https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002Ftester-army\u002Fe2e\u002Fskills\u002Fmigrate-to-e2e\u002FSKILL.md",[],[],"2026-11-11 15:11:14",{"skillNo":77,"name":78,"nameZh":6,"description":79,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":80,"categories":81,"keywords":82,"publishedAt":75},"01M746ZPTAX1Q0FBKAB1CYND7E","e2e","Writes, runs, and debugs end-to-end tests for web and mobile apps with e2e, the agentic test runner, which mixes agent.act goals with exact locator assertions on a Playwright browser or an iOS or Android device. Also drives a project's running app live over the e2e MCP server to look at a screen, check a UI change, take a screenshot or record a video, and try a locator; and runs bug bashes, parallel e2e explore runs with each bug proven by a repro test. Use when a project depends on e2e or has an e2e.config.ts, when asked for end-to-end, browser, mobile, or agentic UI tests, to check a change in the running app, to QA or bug bash an app or branch, or when an e2e run fails.","https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002Ftester-army\u002Fe2e\u002Fskills\u002Fe2e\u002FSKILL.md",[],[],{"skillNo":84,"name":85,"nameZh":6,"description":86,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":87,"categories":88,"keywords":89,"publishedAt":75},"01M746ZJN784HVHMDP9MQGCSGG","create-verification-skill","Generate a project-local verify-\u003Capp> skill on top of e2e, so any coding agent can launch the app, check it is worth driving, exercise a feature the way a user does, keep evidence, and bug bash it. Writes the skill, a feature map with one explore charter per feature, and proves it once. Use for \u002Fcreate-verification-skill, \"make a verification skill for this repo\", or when a project has no scripted way to prove UI behavior.","https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002Ftester-army\u002Fe2e\u002Fskills\u002Fcreate-verification-skill\u002FSKILL.md",[],[],{"skillNo":91,"name":92,"nameZh":6,"description":93,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":94,"categories":95,"keywords":96,"publishedAt":37},"01M716A6ETNB9B1SQ3GXZC75VR","deja-search","deja-vu memory — search the user's past AI coding sessions with the deja CLI. Holds what they already settled and never wrote into the repo (naming rules, banned or already-tried tools, required checks before merge, preferred values, who owns what). Use before your first edit in a task, before you call a change done or ready to merge, before you recommend a tool, library, design or reviewer, before re-debugging an error that may already be solved, when they say 'didn't we fix this before', 'what did we decide about X' or 'I already have that', and before telling them something on this machine does not exist.","https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002Fvshulcz\u002Fdeja-vu\u002Fskills\u002Fdeja-search\u002FSKILL.md",[],[97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116],"search","deja search","never","error","ready","memory","re","decide","fix","design","debugging","when","you","done","reviewer","io","change","cli","merge","past",{"skillNo":118,"name":119,"nameZh":6,"description":120,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":121,"categories":122,"keywords":123,"publishedAt":37},"01M716A3J2W5SENSE9VGMXB375","deja-reflect","End-of-session reflection and memory curation with deja-vu. Review what was decided, what commands broke and what repaired them, and record durable project rules or decisions with deja remember so the next session starts with them.","https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002Fvshulcz\u002Fdeja-vu\u002Fskills\u002Fdeja-reflect\u002FSKILL.md",[],[124,125,126,112,127,128,129,130,131,132,133,134,135,136,102,137,138,139,140,141],"deja reflect","reflect","review","with","record","github","next","https","node","Rules","decisions","brew","remember","project rules","project","session","end","reflection",{"skillNo":143,"name":144,"nameZh":6,"description":145,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":146,"categories":147,"keywords":157,"publishedAt":37},"01M715T3RR4BAYSR57HPPTJ0PF","revid","Make, edit, export and post AI videos with Revid (revid.ai) through the Revid MCP server. Use whenever the user wants a short-form or long-form video made (from an idea, a script, a URL, an article, a PDF, a tweet, a product page, their own clips, or a song), wants to find what is going viral on TikTok, YouTube, Instagram or X before making something, wants to edit or re-export an existing Revid project, generate a standalone AI image or clip, make a series episode, or publish and schedule a finished video to connected social accounts. Covers setup (hosted MCP with OAuth, or an API key), the 36 tools, credits, polling and the rules for publishing. Routes to the more specific revid-* skills when one fits.","https:\u002F\u002Fgithub.com\u002Fhashgraph-online\u002Fawesome-codex-plugins\u002Fblob\u002Fmain\u002Fplugins\u002Ftmakersas\u002Frevid-skills\u002Fskills\u002Frevid\u002FSKILL.md",[148,151,154],{"categoryNo":149,"categoryName":150,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体",{"categoryNo":152,"categoryName":153,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":155,"categoryName":156,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",[144,158,159,160,161,162,163,138,164,103,165,166,167,168,108,169,170,171,172,173],"URL","Video","short-form","standalone","PDF","viral","tiktok","short","social","MCP","song","image","mcp server","schedule","api","publish"]