소스 정보
- 저장소
- dallay/profiletailors.com
- 최근 소스 활동
- 2026년 6월 12일 14:19
- 감지된 SKILL.md 언어
- 영어
- 스타
- 1
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
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