用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/dallay/profiletailors.com --skill kotlin命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Use when writing Playwright tests, fixing flaky tests, debugging failures, implementing Page Object Model, configuring CI/CD, optimizing performance, mocking APIs, handling authentication or OAuth, testing accessibility (axe-core), file uploads/downloads, date/time mocking, WebSockets, geolocation, permissions, multi-tab/popup flows, mobile/responsive layouts, touch gestures, GraphQL, error handling, offline mode, multi-user collaboration, third-party services (payments, email verification), console error monitoring, global setup/teardown, test annotations (skip, fixme, slow), test tags (@smoke, @fast, @critical, filtering with --grep), project dependencies, security testing (XSS, CSRF, auth), performance budgets (Web Vitals, Lighthouse), iframes, component testing, canvas/WebGL, service workers/PWA, test coverage, i18n/localization, Electron apps, or browser extension testing. Covers E2E, component, API, visual, accessibility, security, Electron, and extension testing.
Use when creating features, domain models, use cases, or organizing backend code with Hexagonal Architecture (Ports and Adapters) and CQRS.
Use when bootstrapping a new Spring Boot 4 backend from Spring Initializr, choosing Kotlin + WebFlux + Gradle defaults, defining a hexagonal package-by-feature structure, and wiring local development services for a reactive stack.
基于 SOC 职业分类
正在显示 SKILL.md
| name | kotlin |
| description | Use when working with .kt files, coroutines, or Kotlin-specific patterns. |
| license | Apache-2.0 |
| allowed-tools | Read, Edit, Write, Glob, Grep, Bash |
| metadata | {"author":"profiletailors","version":"1.0"} |
Conventions for writing idiomatic, safe, and maintainable Kotlin code.
.kt files!!STRICTLY AVOID the !! operator. Use safe alternatives:
// ❌ NEVER do this
val name = user!!.name
// ✅ Safe call with elvis
val name = user?.name ?: "Unknown"
// ✅ requireNotNull for asserting
val name = requireNotNull(user?.name) { "User name is required" }
// ✅ let for scoped operations
user?.let {
sendEmail(it.email)
}
// ✅ takeIf/takeUnless for conditional
val activeUser = user.takeIf { it.isActive }
ALWAYS use data classes for immutable models:
data class User(
val id: UserId,
val email: Email,
val name: String,
val isActive: Boolean = true,
val createdAt: Instant = Instant.now(),
)
// ✅ Value classes for type safety
@JvmInline
value class UserId(val value: UUID)
@JvmInline
value class Email(val value: String) {
init {
require(value.contains("@")) { "Invalid email format" }
}
}
Use sealed classes for restricted hierarchies:
sealed interface UiState<out T> {
data class Success<T>(val data: T) : UiState<T>
data class Failure(val error: DomainError) : UiState<Nothing>
data object Loading : UiState<Nothing>
}
// Usage with when (exhaustive)
fun handleState(state: UiState<User>) = when (state) {
is UiState.Success -> displayUser(state.data)
is UiState.Failure -> showError(state.error)
UiState.Loading -> showSpinner()
}
// Domain errors
sealed interface DomainError {
data class NotFound(val id: String) : DomainError
data class Validation(val field: String, val message: String) : DomainError
data object Unauthorized : DomainError
}
STRICTLY AVOID inline fully-qualified class or static references. Favor top-level imports and short names.
// ❌ NEVER do this (inline FQCN)
val id = java.util.UUID.randomUUID()
fun update(@io.swagger.v3.oas.annotations.parameters.RequestBody req: Request)
// ✅ ALWAYS do this
import java . util . UUID
import io . swagger . v3 . oas . annotations . parameters . RequestBody
val id = UUID.randomUUID()
fun update(@RequestBody req: Request)
Exception: Fully-qualified names are ALLOWED and encouraged in KDoc for unambiguous linking.
/**
* Processes a [com.profiletailors.resume.domain.Resume].
*/
See no-fully-qualified-references.md for detailed rules.
This project uses Kotest for expressive, idiomatic Kotlin tests.
Spec Styles: Prefer FunSpec for simple tests, DescribeSpec for BDD-style grouping:
// FunSpec - simple flat structure
class UserServiceTest : FunSpec({
val repository = mockk<UserRepository>()
val service = UserService(repository)
test("should create user with valid data") {
coEvery { repository.save(any()) } returns testUser
val result = service.create(validUserData)
result.shouldNotBeNull()
result.email shouldBe validUserData.email
}
test("should throw when email already exists") {
coEvery { repository.findByEmail(any()) } returns existingUser
shouldThrow<ConflictException> {
service.create(validUserData)
}.message shouldContain "already exists"
}
})
// DescribeSpec - BDD-style grouping
class EmailValueObjectTest : DescribeSpec({
describe("Email") {
context("when created with valid format") {
it("should create successfully") {
val email = Email("user@example.com")
email.value shouldBe "user@example.com"
}
}
context("when created with invalid format") {
it("should throw IllegalArgumentException") {
shouldThrow<IllegalArgumentException> {
Email("invalid-email")
}
}
}
}
})
Common Matchers:
// Equality and nullability
result shouldBe expected
result shouldNotBe null
result.shouldNotBeNull()
result.shouldBeNull()
// Collections
list shouldHaveSize 3
list shouldContain element
list.shouldContainAll(a, b, c)
list.shouldBeEmpty()
// Exceptions
shouldThrow<NotFoundException> { service.findById(unknownId) }
shouldNotThrow { service.findById(validId) }
// String matchers
name shouldStartWith "John"
error.message shouldContain "not found"
Prefer Result<T> over exceptions for business logic:
// ✅ Return Result for operations that can fail
fun findUser(id: UserId): Result<User> = runCatching {
userRepository.findById(id)
?: throw NotFoundException("User not found")
}
// ✅ Chain operations
fun processUser(id: UserId): Result<ProcessedUser> {
return findUser(id)
.mapCatching { user -> validate(user) }
.mapCatching { validated -> enrich(validated) }
.onFailure { logger.error(it) { "Failed to process user $id" } }
}
// ✅ Handle result
findUser(userId).fold(
onSuccess = { user -> Response.ok(user) },
onFailure = { error -> Response.error(error.message) },
)
Embrace structured concurrency:
// ✅ Suspend functions for async operations
suspend fun fetchUser(id: UserId): User {
return withContext(Dispatchers.IO) {
userRepository.findById(id)
}
}
// ✅ Flow for streams
fun observeUsers(): Flow<List<User>> = flow {
while (true) {
emit(userRepository.findAll())
delay(5.seconds)
}
}.flowOn(Dispatchers.IO)
// ✅ Proper scope management
class UserService(
private val scope: CoroutineScope,
) {
fun startSync() {
scope.launch {
observeUsers().collect { users ->
processUsers(users)
}
}
}
}
Use for enhancing existing types:
// ✅ Domain-specific extensions
fun String.toSlug(): String =
lowercase()
.replace(Regex("[^a-z0-9\\s-]"), "")
.replace(Regex("\\s+"), "-")
fun Instant.isRecent(threshold: Duration = 24.hours): Boolean =
this.isAfter(Instant.now().minus(threshold))
// ✅ Null-safe extensions
fun String?.orEmpty(): String = this ?: ""
fun <T> List<T>?.orEmpty(): List<T> = this ?: emptyList()
| Element | Convention | Example |
|---|---|---|
| Classes/Interfaces | PascalCase | UserService, WorkspaceRepository |
| Functions/Variables | camelCase | findById, userName |
| Constants | UPPER_SNAKE_CASE | MAX_RETRY_COUNT, DEFAULT_TIMEOUT |
| Test Methods | Backticks | `should return user when exists` |
| Booleans | is, has, are prefix | isActive, hasPermission, areValid |
// ✅ Expression body for simple functions
fun double(x: Int): Int = x * 2
// ✅ Trailing commas (helps with diffs)
data class Config(
val host: String,
val port: Int,
val timeout: Duration, // ← trailing comma
)
// ✅ Named arguments for 3+ parameters
createUser(
name = "John",
email = "john@example.com",
role = Role.USER,
)
// ✅ Prefer val over var
val items = mutableListOf<Item>() // val for reference, mutable for content
import io.github.oshai.kotlinlogging.KotlinLogging
private val logger = KotlinLogging.logger {}
class UserService {
fun processUser(user: User) {
logger.info { "Processing user: ${user.id}" }
try {
// logic
} catch (e: ProcessingException) {
logger.error(e) { "Failed to process user: ${user.id}" }
throw e
}
}
}
// ❌ NEVER log sensitive data
logger.info { "User logged in: password=${user.password}" } // WRONG!
❌ !! operator - Use ?., ?:, requireNotNull, let
❌ Catching generic Exception - Catch specific exceptions
❌ var when val works - Prefer immutability
❌ Inheritance over composition - Prefer composition
❌ Wildcard imports - Except java.util.*, io.mockk.*
❌ Mutable public properties - Use private set or immutable data
# Run tests
./gradlew test
# Run specific test class
./gradlew test --tests "com.profiletailors.user.UserServiceTest"
# Run tests by tag
./gradlew test -PincludeTags=unit
# Lint with Detekt
./gradlew detektAll
# Build
./gradlew build