ECC 的 Cursor Kotlin 规则:kotlin-patterns.md 如何为 .kt 文件注入 Kotlin 惯用模式约束
本文以 ECC 仓库中 kotlin-patterns.md 规则文件为主体,完整拆解 Cursor 规则的前置元数据(globs 匹配与按需加载机制)、Sealed 类、扩展函数、作用域函数与 Koin 依赖注入四大 Kotlin 模式约束,并结合同目录规则族、kotlin-patterns skill 与 Cursor 安装目标 的源码,说明这条规则在 ECC agent harness 中如何被触发、如何延伸、又如何分发到实际项目中。读完你可以掌握:为 Cursor 编写语言级规则文件的方法、规则与 skill 的分层协作关系,以及一套可直接落地的 Kotlin 编码约束。
一、规则文件本体:一个“按需加载”的 Kotlin 模式约束
.cursor/rules/kotlin-patterns.md 是 ECC 为 Cursor 编辑器提供的 Kotlin 语言规则文件。与 alwaysApply: true 的全局规则不同,它通过前置元数据声明自己的适用范围,只在编辑 Kotlin 相关文件时才注入上下文:
---
description: "Kotlin patterns extending common rules"
globs: ["**/*.kt", "**/*.kts", "**/build.gradle.kts"]
alwaysApply: false
---
三个字段的含义:
globs:文件匹配模式。**/*.kt与**/*.kts覆盖 Kotlin 源码和 Kotlin 脚本,**/build.gradle.kts把 Gradle Kotlin DSL 构建脚本也纳入约束——这与 kotlin-patterns skill 中“Configuring Gradle Kotlin DSL builds”的使用场景对应;alwaysApply: false:规则不随会话常驻,仅在 globs 命中时被加载,避免污染其他语言任务的上下文预算;description:向 Agent 说明该规则定位——“在 common 规则基础上扩展 Kotlin 专属内容”。
从文件结构看,正文以一行提示 This file extends the common patterns rule with Kotlin-specific content 开篇,明确它是一条增量规则:基础约束来自 common-patterns.md,本文件只补充 Kotlin 专属部分。这种“common 打底 + 语言扩展”的分层写法在 .cursor/rules/ 目录中是统一模式,Kotlin 规则族共五个文件:
| 规则文件 | 覆盖主题 |
|---|---|
| kotlin-coding-style.md | 格式化(ktfmt/ktlint)、不可变性、空安全、表达式体 |
| kotlin-patterns.md | Sealed 类、扩展函数、作用域函数、依赖注入 |
| kotlin-testing.md | Kotest + MockK、runTest 协程测试、Kover 覆盖率 |
| kotlin-security.md | Kotlin 安全约束 |
| kotlin-hooks.md | Kotlin 相关的 hook 配置 |
五个文件使用完全相同的 globs 声明,意味着打开任意 .kt 文件时,这一整套约束会同时生效——模式、风格、测试、安全形成闭环。
二、Sealed Classes:用密封类型建模穷举层级
规则给出的核心示例是泛型 Result 类型:
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Failure(val error: AppError) : Result<Nothing>()
}
要点解析:
sealed class声明受限制的继承层级,所有子类必须在同一文件内定义,使when表达式可以做到编译期穷举——遗漏分支直接报错,而不是运行时才暴露;out T是泛型协变声明,允许Success<Nothing>这类子类型关系成立;Failure声明为Result<Nothing>,表达“失败分支不携带成功数据”这一语义,Nothing是 Kotlin 的底部类型(top-level 中任何类型都是它的子类型)。
这与 common 规则中 API Response Format 的“统一响应包络”思想一脉相承:用类型系统而非约定俗成的字段命名来强制 success/data/error 的互斥关系。
在 ECC 的 kotlin-patterns skill 中,这个模式被进一步扩展为带 Loading 分支的三态版本,并配上了穷举消费函数:
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Failure(val error: AppError) : Result<Nothing>()
data object Loading : Result<Nothing>()
}
fun <T> Result<T>.getOrNull(): T? = when (this) {
is Result.Success -> data
is Result.Failure -> null
is Result.Loading -> null
}
skill 中还给出了 sealed interface 建模 API 错误的进阶用法(ApiError.NotFound/Unauthorized/Validation/Internal 各自映射到 404/401/422/500 状态码),展示了同一“穷举层级”思想在错误域的应用。规则文件给出最小可用形态,skill 给出完整版——这正是 ECC “规则管底线、skill 管深度”的分层设计。
三、Extension Functions:不继承、不污染的全局扩展
规则给出的示例是把行为“加”到 String 上,且限定在使用的地方:
fun String.toSlug(): String =
lowercase().replace(Regex("[^a-z0-9\\s-]"), "").replace(Regex("\\s+"), "-")
“scoped to where they're used” 这句约束的含义,在 skill 的示例中有明确落地——作用域化的扩展函数:把扩展函数声明为类内部的 private 成员,避免污染全局命名空间:
class UserService {
private fun User.isActive(): Boolean =
status == Status.ACTIVE && lastLogin.isAfter(Instant.now().minus(30, ChronoUnit.DAYS))
fun getActiveUsers(): List<User> = userRepository.findAll().filter { it.isActive() }
}
此外 skill 补充了两个实用形态:带默认参数的时间转换扩展(Instant.toLocalDate(zone))和集合扩展(List<T>.secondOrNull() 基于标准库 getOrNull(1) 一行实现),说明规则约束的是“扩展函数应当领域化、私有化、可测试”,而非仅仅“可以用扩展函数”。
四、Scope Functions:四种作用域函数的分工与反模式
规则对五个作用域函数给出了明确的职责划分(规则原文列了三个,skill 补全了 run/with):
let:转换可空或受限结果,返回 lambda 结果——val length: Int? = name?.let { it.trim().length }apply:配置对象,返回对象本身——User().apply { name = "Alice"; email = "..." }also:副作用(日志、埋点),返回对象本身——createUser(request).also { logger.info("Created user: ${it.id}") }run:带接收者的块执行,返回 lambda 结果——connection.run { prepareStatement(sql); executeQuery() }with:run的非扩展形式——with(StringBuilder()) { appendLine(...); toString() }
规则明确禁止的一条反模式是嵌套作用域函数,skill 给出了对照示例:
// Bad: Nesting scope functions
user?.let { u ->
u.address?.let { addr ->
addr.city?.let { city -> println(city) } // 嵌套三层,可读性崩塌
}
}
// Good: Chain safe calls instead
val city = user?.address?.city
city?.let { println(it) }
这条约束与同目录 kotlin-coding-style.md 中“避免 !!,使用 ?.、?:、require”的空安全条款互相配合:安全调用链优先,作用域函数只在确有“转换/配置/副作用”语义时使用。
五、Dependency Injection:Koin 模块在 Ktor 项目中的声明方式
规则给出的是 Koin 声明式模块,且特意绑定 Ktor 场景(规则原文):
val appModule = module {
single<UserRepository> { ExposedUserRepository(get()) }
single { UserService(get()) }
}
single<T>注册单例,{ ExposedUserRepository(get()) }中的get()在装配时从容器解析依赖(这里解析的是数据库连接/事务对象),实现构造器注入而非字段注入;single { UserService(get()) }隐式按类型注册UserService,get()拉取UserRepository;- 从 skill 的 Gradle Kotlin DSL 配置 看,ECC 的推荐技术栈组合是 Ktor 3.4.0 + Exposed 1.0.0 + Koin 4.2.0(
io.insert-koin:koin-ktor)+ kotlinx-coroutines 1.10.2,Koin 模块即作为该栈的标准 DI 层。
规则层面更完整的版本(来自 rules/kotlin/patterns.md,它是本规则在非 Cursor 平台的对应物)区分了两种 DI 选型:KMP 项目用 Koin,纯 Android 项目用 Hilt:
// Koin — declare modules
val dataModule = module {
single<ItemRepository> { ItemRepositoryImpl(get(), get()) }
factory { GetItemsUseCase(get()) }
viewModelOf(::ItemListViewModel)
}
注意 factory(每次解析新建,适合无状态 UseCase 与 ViewModel 之外的短生命周期对象)与 single 的生命周期区别,这是规则文件最小示例中未展开、但实操中必须理解的参数差异。
六、从规则到 Skill:kotlin-patterns 的引用闭环
规则文件末尾的 Reference 段落指向 skill:
See skill:
kotlin-patternsfor comprehensive Kotlin patterns including coroutines, DSL builders, and delegation.
在 ECC 的架构中,.cursor/rules/ 是常驻约束层(短小、可按 globs 自动加载),skills/ 是深度知识层(按需激活的完整手册)。两者内容同源且互相呼应:
- 规则文件的 Sealed 类示例(两态
Result)是 skill 中三态Result的简化版; - 规则文件的
toSlug()扩展函数在 skill 中补齐了.trim('-')收尾和“作用域化扩展”最佳实践; - 规则文件未覆盖的协程、DSL、委托三大主题,全部由 skill 承接。
skill 额外提供的关键模式(读者若需完整 Kotlin 工程约束应一并阅读 skills/kotlin-patterns/SKILL.md):
结构化并发——coroutineScope 并行取数、supervisorScope 让子任务失败互相独立:
suspend fun fetchUserWithPosts(userId: String): UserProfile =
coroutineScope {
val user = async { userService.getUser(userId) }
val posts = async { postService.getUserPosts(userId) }
UserProfile(user = user.await(), posts = posts.await())
}
类型安全 DSL Builder——用 @DslMarker 防止隐式接收者歧义:
@DslMarker
annotation class HtmlDsl
@HtmlDsl
class HTML {
fun body(init: Body.() -> Unit) { children += Body().apply(init) }
// ...
}
fun html(init: HTML.() -> Unit): HTML = HTML().apply(init)
接口委托——UserRepository by delegate 一行实现透传,只覆写需要加日志的方法。
此外 skill 末尾的“Quick Reference”表格把 16 条 Kotlin 惯用法(val over var、value class、when 表达式、Flow、sequence 懒求值、by 委托等)汇总为速查表,可直接作为 code review 的 checklist 使用。
七、配套约束:构建配置与测试规则如何咬合
Kotlin 规则族中另外两个文件为 patterns 规则提供了执行保障:
构建层:kotlin-patterns skill 给出了完整的 build.gradle.kts 参考配置,关键项包括:
plugins {
kotlin("jvm") version "2.3.10"
id("io.ktor.plugin") version "3.4.0"
id("org.jetbrains.kotlinx.kover") version "0.9.7"
id("io.gitlab.arturbosch.detekt") version "1.23.8"
}
kotlin { jvmToolchain(21) }
detekt {
config.setFrom(files("config/detekt/detekt.yml"))
buildUponDefaultConfig = true
}
globs 中包含 **/build.gradle.kts 的意义在此体现:规则不仅约束业务代码,也约束构建脚本本身的写法(Kotlin DSL 风格、依赖版本管理)。
测试层:kotlin-testing.md 规定使用 Kotest(StringSpec/FunSpec/BehaviorSpec 风格)+ MockK 做 mock,协程代码统一用 runTest:
test("async operation completes") {
runTest {
val result = service.fetchData()
result.shouldNotBeEmpty()
}
}
覆盖率由 Kover 报告。这意味着 patterns 规则里要求写出的 Result<T> 返回值,在测试规则里有对应的验证手段——suspend fun getById(id: String): Result<Item> 这类接口(见 rules/kotlin/patterns.md 的 Repository 模式)天然适合 Kotest 断言。
八、分发机制:规则如何进入用户项目
从源码结构看,.cursor/rules/ 并非仅供本仓库使用,而是 ECC 安装器面向 Cursor 平台的分发源。scripts/lib/install-targets/cursor-project.js 中存在 sourceRelativePath: '.cursor/rules' 的声明,将规则目录整体作为安装操作的目标路径之一;tests/lib/install-targets.test.js 中的断言(如检查 .cursor/rules/common-coding-style.md、common-agents.md 等文件)验证了安装清单对这些规则文件的覆盖,并处理了“平台规则与原生 .cursor/rules 内容冲突时优先保留原生内容”的逻辑。
换言之,当开发者通过 ECC 的安装流程把 harness 部署到一个 Kotlin/Ktor 项目时,kotlin-patterns.md 会随规则族落入目标项目的 .cursor/rules/,此后 Cursor 中的 Agent 在触碰任何 .kt 文件时即自动获得本文第二节至第五节的全部约束——无需在每条提示中手动复述团队约定。
九、速查与落地建议
结合规则文件本体与 skill 的完整版,可提炼出 Kotlin 项目的最小约束集:
| 约束 | 落地方式 | 依据 |
|---|---|---|
| 穷举类型层级 | sealed class/interface + 穷举 when,失败态用 Nothing |
kotlin-patterns.md |
| 扩展函数私有化 | 领域扩展声明为类内 private 成员函数 |
SKILL.md Extension Functions 节 |
| 作用域函数不嵌套 | 优先 ?. 链,let/apply/also/run/with 按语义选择 |
SKILL.md Scope Functions 节 |
| DI 构造器注入 | KMP 用 Koin(single/factory 区分生命周期),Android 用 Hilt |
rules/kotlin/patterns.md |
| 协程测试 | runTest + Kotest + MockK,Kover 出覆盖率 |
kotlin-testing.md |
| 构建脚本同受约束 | globs 覆盖 build.gradle.kts,detekt 静态检查 |
SKILL.md Gradle Kotlin DSL 节 |
最后需要说明适用前提:本文所有版本与依赖坐标(Kotlin 2.3.10、Ktor 3.4.0、Koin 4.2.0 等)均以当前仓库 skill 文档中记录的值为准;规则文件本身只声明约束语义,不绑定具体依赖版本,实际项目中应按自身技术栈调整,但“common 打底、语言规则扩展、skill 承接深度”这一三层结构可以直接照搬到其他语言的规则体系建设中。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00