Token导航 LogoToken导航TokenDH.com
前端设计需要联网github未标认证来源可访问许可证需确认审计通过

kotlin-context-dikotlin 上下文 di

Agent Skill

kotlin-context-di 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

336

周安装

14

GitHub Stars

13

下载量

112
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:kotlin-context-di(kotlin 上下文 di)
来源仓库:https://github.com/anderssv/the-example
仓库路径:skills/kotlin-context-di
安装命令:
npx skills add https://github.com/anderssv/the-example --skill kotlin-context-di
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/anderssv/the-example --skill kotlin-context-di

简介

kotlin-context-di 用于处理 Kotlin 上下文依赖注入相关的 GitHub 协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中围绕仓库状态、Issue 或 PR 进行整理和分析。

  • 适用于模块化架构、测试隔离或服务生命周期管理场景,可结合项目结构提供 DI 容器配置建议。
  • 通过 npx skills add 命令从指定 GitHub 仓库安装,需确认是否集成特定 DI 框架或仅提供模式识别能力。
  • 使用前应检查仓库维护状态、权限范围,并注意是否会触发文件读写或命令执行,避免影响本地环境或触发安全策略。
  • 适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

SKILL.md

STARTER_CHARACTER = 🔌

Manual Dependency Injection with AppDependencies, SystemContext and TestContext

Structure Kotlin applications using manual DI with an interface-first contract (AppDependencies), a production context (SystemContext), and a standalone test context (SystemTestContext). This approach provides type-safe dependency management, full control over initialization, and excellent testability without framework overhead.

Core Pattern: AppDependencies Interface

Define an interface as the contract for all application dependencies. Use nested interfaces to group related components:

interface AppDependencies {
    interface Repositories {
        val customerRepo: CustomerRepository
        val orderRepo: OrderRepository
    }

    interface Clients {
        val paymentClient: PaymentClient
        val emailClient: EmailClient
    }

    interface Services {
        val customerService: CustomerService
        val orderService: OrderService
    }

    val repositories: Repositories
    val clients: Clients
    val services: Services
    val clock: Clock
}

Both production and test contexts implement this interface independently — no inheritance between them.

Why an interface (not an open class):

  • Open classes with constructor parameters force test subclasses to satisfy those parameters, even when test fakes never use them (e.g., creating a dummy DataSource just to satisfy a constructor)
  • lazy doesn't protect against production initialization in subclasses — overriding an eager val in a subclass does NOT prevent the base class initializer from running
  • Interfaces have no constructors and no inherited behavior — test implementations must explicitly provide every dependency
  • Each context handles its own initialization independently — no lazy needed

Production: SystemContext

A plain class implementing AppDependencies — no open, no lazy, eager val initialization throughout. Infrastructure (DataSource, credentials, JWKS) lives directly on SystemContext, not exposed through AppDependencies. Grouping implementations are anonymous objects:

class SystemContext(
    private val config: Config,
) : AppDependencies {
    // Infrastructure — not exposed through AppDependencies
    private val dataSource = HikariDataSource(config.dbConfig)

    override val clock: Clock = Clock.systemDefaultZone()

    override val repositories = object : AppDependencies.Repositories {
        override val customerRepo: CustomerRepository = CustomerRepositoryImpl(dataSource)
        override val orderRepo: OrderRepository = OrderRepositoryImpl(dataSource)
    }

    override val clients = object : AppDependencies.Clients {
        override val paymentClient: PaymentClient = PaymentClientImpl(config.paymentApiKey)
        override val emailClient: EmailClient = EmailClientImpl(config.smtpConfig)
    }

    override val services = object : AppDependencies.Services {
        override val customerService = CustomerService(repositories.customerRepo)
        override val orderService = OrderService(
            repositories.orderRepo,
            clients.paymentClient,
            clients.emailClient,
        )
    }
}

Key characteristics:

  • No open — this class is not designed for extension
  • No lazy — eager initialization throughout
  • No default values in production config — all config must be explicit per environment
  • Infrastructure captured by anonymous objects from the enclosing scope
  • Anonymous objects keep production wiring in one place

Test: SystemTestContext (Standalone)

A standalone class implementing AppDependencies — does NOT extend SystemContext. Uses inner classes for groupings (access to enclosing context properties). Covariant override inference means concrete fake types are available directly — no dual-access needed:

class SystemTestContext(
    dataSource: DataSource? = null,
) : AppDependencies {

    override val clock = TestClock.now()

    inner class TestRepositories : AppDependencies.Repositories {
        override val customerRepo = CustomerRepositoryFake()   // concrete type!
        override val orderRepo = OrderRepositoryFake()         // concrete type!
    }

    inner class TestClients : AppDependencies.Clients {
        override val paymentClient = PaymentClientFake()       // concrete type!
        override val emailClient = EmailClientFake()           // concrete type!
    }

    override val repositories =
        if (dataSource != null) {
            object : AppDependencies.Repositories {
                override val customerRepo = CustomerRepositoryImpl(dataSource)
                override val orderRepo = OrderRepositoryImpl(dataSource)
            }
        } else {
            TestRepositories()
        }

    override val clients = TestClients()

    override val services = object : AppDependencies.Services {
        override val customerService = CustomerService(repositories.customerRepo)
        override val orderService = OrderService(
            repositories.orderRepo,
            clients.paymentClient,
            clients.emailClient,
        )
    }
}

Key characteristics:

  • Does NOT extend SystemContext — no inheritance between production and test
  • Inner class for groupings — allows access to enclosing context properties
  • Covariant override inferenceoverride val repositories = TestRepositories() infers the concrete type, so repositories.customerRepo resolves to CustomerRepositoryFake in test scope
  • No dual-access needed — no separate testRepositories vs repositories
  • Constructor injection with defaultsSystemTestContext(dataSource = realDs) for integration, no-arg for unit tests

Covariant Override Inference

This is the key mechanism that eliminates dual-access properties. When SystemTestContext declares:

override val repositories = TestRepositories()

Kotlin infers the property type as TestRepositories (the concrete type), not AppDependencies.Repositories (the interface type). When test code uses with(SystemTestContext()), the receiver type is SystemTestContext, and repositories.customerRepo resolves to CustomerRepositoryFake.

In tests — direct access to fake methods, no casting needed:

@Test
fun testOrderCreation() {
    with(SystemTestContext()) {
        // Act
        services.orderService.createOrder(customerId, items)

        // Assert — direct access to fake methods via covariant inference
        assertThat(repositories.orderRepo.getSavedOrders())
            .contains(order)
    }
}

Fresh Context Per Test

Create a fresh context per test when fakes are stateful (the common case):

@Test
fun `should save order`() {
    with(SystemTestContext()) {
        services.orderService.createOrder(request)
        assertThat(repositories.orderRepo.getSavedOrders()).hasSize(1)
    }
}

@Test
fun `should not save order when payment fails`() {
    with(SystemTestContext()) {
        clients.paymentClient.failOnNextCharge()
        services.orderService.createOrder(request)
        assertThat(repositories.orderRepo.getSavedOrders()).isEmpty()
    }
}

Why: Fakes are stateful — OrderRepositoryFake accumulates saved orders, EmailClientFake accumulates sent emails. Sharing a context across tests causes state from one test to leak into the next, leading to order-dependent failures and flaky tests.

The with(SystemTestContext()) {...} pattern is idiomatic, cheap (no real I/O), and prevents test pollution.

Share a context only when fakes are truly stateless or when you have explicit reset logic — this is uncommon.

E2E: Delegation for Partial Overrides

For end-to-end tests that need to replace specific services while keeping the rest intact, use Kotlin's delegation:

val testContext = SystemTestContext()
val dependencies = object : AppDependencies by testContext {
    override val services = object : AppDependencies.Services by testContext.services {
        override val orderService = customOrderService
    }
}

This creates a new AppDependencies that delegates everything to testContext except services.orderService, which is replaced with a custom implementation.

Nullable-to-Non-nullable Narrowing in Tests

When production interfaces have nullable dependencies (because configuration may be absent), test implementations can narrow them to non-nullable:

// Production interface — nullable because config may not exist
interface Clients {
    val authClient: AuthClient?
    val notificationClient: NotificationClient?
}

// Test implementation — non-nullable
inner class TestClients : AppDependencies.Clients {
    override val authClient = AuthClientStub()            // non-nullable!
    override val notificationClient = NotificationClientStub()  // non-nullable!
}

This is valid Kotlin because non-nullable types are subtypes of nullable types. Tests never need null checks when accessing test clients, even though production code handles the nullable case. This is a significant ergonomic win — test code stays clean and focused on behavior.

Route Functions Accept AppDependencies

Route functions (or controllers) accept the AppDependencies interface, not the context object — destructure inside:

fun Application.orderRoutes(deps: AppDependencies) {
    routing {
        get("/orders/{id}") {
            val orderId = call.parameters["id"]!!
            val order = deps.services.orderService.getOrder(orderId)
            call.respond(order)
        }

        post("/orders") {
            val request = call.receive<CreateOrderRequest>()
            val order = deps.services.orderService.createOrder(request)
            call.respond(order)
        }
    }
}

Type Safety Benefits

Compile-time checking:

  • Typos caught immediately
  • Refactoring tools work perfectly (rename, move, find usages)
  • Missing dependencies fail at compile time, not runtime

IDE support:

  • Full autocomplete for all dependencies
  • Jump to definition works seamlessly
  • No string-based lookups or reflection

Clear dependency graph:

  • Constructor parameters show exact dependencies
  • Easy to trace where any component is used
  • No hidden framework magic

Integration with Test Doubles

TestContext typically contains Fakes (in-memory implementations of interfaces):

class CustomerRepositoryFake : CustomerRepository {
    private val db = mutableMapOf<String, Customer>()

    override fun save(customer: Customer) {
        db[customer.id] = customer
    }

    override fun findById(id: String): Customer? {
        return db[id]
    }

    // Test-specific methods (not in interface)
    fun getSavedCustomers(): List<Customer> = db.values.toList()
    fun failOnNextSave() { /* ... */ }
}

The TestContext wires these Fakes and exposes them with concrete types via covariant override inference:

class SystemTestContext : AppDependencies {
    inner class TestRepositories : AppDependencies.Repositories {
        override val customerRepo = CustomerRepositoryFake()  // concrete type
    }

    override val repositories = TestRepositories()  // inferred as TestRepositories
}

Now services.customerService uses CustomerRepositoryFake automatically because it references repositories.customerRepo, and tests access fake-specific methods via repositories.customerRepo without casting — the covariant inference gives you the concrete type.

Application Wiring

Main entry point:

fun main() {
    val context = SystemContext(Config.fromEnvironment())

    val app = Application(
        context.services.orderService,
        context.services.userService,
    )

    app.start()
}

Web framework integration (Ktor example):

fun Application.module() {
    val context = SystemContext(Config.fromEnvironment())

    orderRoutes(context)
    userRoutes(context)
}

Routes accept AppDependencies, not the context object. No framework-specific annotations or registrations needed.

Why This Pattern Works

Simplicity:

  • No annotations to learn
  • No configuration files
  • No classpath scanning or reflection
  • Plain Kotlin code

Debuggability:

  • Step through initialization in debugger
  • Set breakpoints in context creation
  • No framework magic hiding behavior

Readability:

  • Dependencies visible in one place
  • Constructor calls show exactly what's needed
  • No surprising behavior from framework lifecycle

Test control:

  • Full control over what gets loaded
  • Fast test startup (only load what you need)
  • Easy to inject test doubles
  • No special test runners or annotations
  • No casting needed to access test-specific methods

Flexibility:

  • Change initialization order easily
  • Add conditional logic (feature flags, environment checks)
  • Compose contexts using delegation

Scalability:

  • Pattern stays simple as project grows
  • More dependencies just mean more properties in context classes
  • No framework limitations or architectural constraints

Anti-patterns

Avoid using open classes for dependency grouping:

// Don't do this — forces test subclasses to satisfy constructor parameters
open class Repositories(private val dataSource: DataSource) {
    open val customerRepo: CustomerRepository = CustomerRepositoryImpl(dataSource)
}

Use interfaces instead — they have no constructors and force explicit implementation.

Avoid lazy in production context:

// Don't do this — lazy doesn't protect against production initialization in subclasses
open class SystemContext {
    open val repositories by lazy { ... }
}

Lazy adds complexity and gives false security. With interface + standalone implementations, each context initializes independently.

Avoid inheritance between production and test contexts:

// Don't do this
class SystemTestContext : SystemContext() {  // Inherits production initialization!
    override val repositories = TestRepositories()
}

Use standalone classes that both implement the AppDependencies interface.

Avoid casting to access test-specific methods:

// Don't do this
val emailClient = clients.emailClient as EmailClientFake
assertThat(emailClient.sentEmails).hasSize(1)

Use covariant override inference — inner class groupings give you concrete types automatically.

Avoid dual-access properties:

// Don't do this
val testRepositories = TestRepositories()
override val repositories: Repositories get() = testRepositories

With standalone test context and covariant override inference, repositories already resolves to TestRepositories.

Avoid deep context hierarchies:

// Too complex
open class DatabaseContext : InfrastructureContext()
open class RepositoryContext : DatabaseContext()
open class ServiceContext : RepositoryContext()
open class SystemContext : ServiceContext()

Keep it flat: one AppDependencies interface with nested interface groups for organization.

Don't mix with annotation-based DI:

// Don't mix patterns
@Inject lateinit var customerService: CustomerService  // Framework DI
val orderService = OrderService(repositories.orderRepo)  // Manual DI

Choose one approach and stick with it.

Migration Path

Adding to existing project:

  1. Create AppDependencies interface with nested grouping interfaces
  2. Create SystemContext implementing it with existing components
  3. Wire main entry point to use context
  4. Gradually move initialization logic into context
  5. Create SystemTestContext and migrate tests incrementally

From framework DI:

  1. Create parallel AppDependencies + SystemContext alongside framework
  2. New code uses the interface-first pattern
  3. Gradually migrate existing code
  4. Remove framework once migration complete

No big-bang rewrite required. Adopt incrementally.

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

37.36%
按下载量换算42

Claude

29.47%
按下载量换算33

Cursor

17.35%
按下载量换算19

Gemini CLI

9.31%
按下载量换算10

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills