본문으로 건너뛰기
cccskills
무료GitHub에 공개

feature-option

Guides step-by-step through all 11 touchpoints for adding a new @Fake annotation option (like mutability or callHistory). Covers annotation enum, CLI processor, Gradle plugin, FIR metadata, FIR translator, the shared FakeDeclaration model, and code generators. Use when adding a new option to @Fake, adding a new enum to the annotation, making something configurable per-interface, implementing a new compiler plugin feature flag, adding a new Gradle extension property, or extending the three-tier resolution (annotation → plugin → default). Make sure to use this skill whenever a change touches more than one layer of the option pipeline, even for small additions like a new enum value.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rsicarelli/fakt스타 67(GitHub 67 + cccskills 0)

실행 점수 미검증

설치 방법 보기

포함된 파일(1)

  • SKILL.md10.1 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Feature Option Guide

Adds a new option to the @Fake annotation, following the confirmed pattern from mutability and callHistory.

Instructions

1. Gather Option Details

Use AskUserQuestion to collect:

  • Option name (e.g., threadSafety)
  • Enum values — always DEFAULT plus two semantically opposite values (e.g., ENABLED/DISABLED)
  • Plugin-level default — the boolean value when annotation says DEFAULT (e.g., true or false)
  • What it controls — which generators are affected

2. Walk Through All 11 Layers

Guide the implementation in this exact order. Each layer depends on the previous.


Layers 1-2 define the user-facing API that developers interact with — the enum they import and the annotation parameter they set.

Layer 1 — Annotation Enum (new file)

File: annotations/src/commonMain/kotlin/com/rsicarelli/fakt/{OptionName}Mode.kt

public enum class {OptionName}Mode {
    DEFAULT,   // Follow plugin-level setting
    {VALUE_A}, // Explicit enable
    {VALUE_B}, // Explicit disable
}

Pattern: MutabilityMode(DEFAULT, IMMUTABLE, MUTABLE), CallHistoryMode(DEFAULT, ENABLED, DISABLED).

Layer 2 — @Fake Annotation Parameter

File: annotations/src/commonMain/kotlin/com/rsicarelli/fakt/Fake.kt

Add parameter with DEFAULT default:

public annotation class Fake(
    val callHistory: CallHistoryMode = CallHistoryMode.DEFAULT,
    val mutability: MutabilityMode = MutabilityMode.DEFAULT,
    val {optionName}: {OptionName}Mode = {OptionName}Mode.DEFAULT, // NEW
)

Naming note: Real field names are descriptive and may not follow the template literally. E.g., mutability uses enableMutableFakes (not enableMutability) and generateMutableBehaviors (not generateMutability). Choose names that best describe the feature's effect.

Layers 3-4 wire the option through the Gradle build system to the compiler — this is how users set project-wide defaults in build.gradle.kts.

Layer 3 — Gradle Plugin Extension

File: gradle-plugin/src/main/kotlin/com/rsicarelli/fakt/gradle/FaktPluginExtension.kt

Add Property<Boolean> with convention default:

public val enable{OptionName}: Property<Boolean> =
    objects.property(Boolean::class.java).convention({defaultValue})

Layer 4 — Gradle Subplugin (passes option to compiler)

File: gradle-plugin/src/main/kotlin/com/rsicarelli/fakt/gradle/FaktGradleSubplugin.kt

In applyToCompilation, add SubpluginOption:

add(SubpluginOption(key = "enable{OptionName}", value = extension.enable{OptionName}.get().toString()))

Layers 5-6 register the option with compiler plugin infrastructure — the CLI processor receives the value from Gradle, and FaktOptions makes it available throughout the compiler.

Layer 5 — Compiler CLI Processor

File: compiler/src/main/kotlin/com/rsicarelli/fakt/compiler/core/config/FaktCommandLineProcessor.kt

Three additions in companion object + one in processOption:

// Companion:
val ENABLE_{OPTION}_KEY = CompilerConfigurationKey<Boolean>("fakt.enable{OptionName}")
val ENABLE_{OPTION}_OPTION = CliOption(
    optionName = "enable{OptionName}",
    valueDescription = "true|false",
    description = "Enable {description} by default (default: {defaultValue})",
    required = false,
)

// Add to pluginOptions list:
ENABLE_{OPTION}_OPTION

// In processOption when-branch:
"enable{OptionName}" -> configuration.put(ENABLE_{OPTION}_KEY, value.toBoolean())

Layer 6 — FaktOptions Data Class

File: compiler/src/main/kotlin/com/rsicarelli/fakt/compiler/core/config/FaktOptions.kt

Add field + read in load():

data class FaktOptions(
    // ...existing...
    val enable{OptionName}Default: Boolean = {defaultValue},
)

// In load():
val enable{OptionName} = configuration.get(FaktCommandLineProcessor.ENABLE_{OPTION}_KEY) ?: {defaultValue}
return FaktOptions(/* ...existing... */, enable{OptionName}Default = enable{OptionName})

Layers 7-8 extract the annotation value at compile time during the FIR phase — mirror enums avoid a runtime dependency on the annotations module, and checkers read the actual @Fake parameter value from source code.

Layer 7 — FIR Mirror Enum + Validated Metadata

File: codegen-runtime/src/main/kotlin/com/rsicarelli/fakt/compiler/fir/metadata/FirFakeMetadata.kt

// Mirror enum (avoids runtime dep on annotations):
enum class Fir{OptionName}Mode { DEFAULT, {VALUE_A}, {VALUE_B} }

// Add to BOTH ValidatedFakeInterface and ValidatedFakeClass:
val {optionName}Mode: Fir{OptionName}Mode = Fir{OptionName}Mode.DEFAULT,

Layer 8 — FIR Checkers (annotation extraction)

Files: compiler/.../fir/checkers/FakeInterfaceChecker.kt + FakeClassChecker.kt

Add private extraction method in both:

private fun extract{OptionName}Mode(declaration: FirClass, session: FirSession): Fir{OptionName}Mode {
    val fakeAnnotation = declaration.annotations.find { annotation ->
        annotation.toAnnotationClassIdSafe(session) == FAKE_ANNOTATION_CLASS_ID
    } ?: return Fir{OptionName}Mode.DEFAULT

    val arg = fakeAnnotation.argumentMapping.mapping.entries
        .find { it.key.asString() == "{optionName}" }?.value

    return when (arg) {
        is FirPropertyAccessExpression -> {
            val enumEntry = arg.calleeReference.toResolvedEnumEntrySymbol()
            when (enumEntry?.name?.asString()) {
                "{VALUE_A}" -> Fir{OptionName}Mode.{VALUE_A}
                "{VALUE_B}" -> Fir{OptionName}Mode.{VALUE_B}
                else -> Fir{OptionName}Mode.DEFAULT
            }
        }
        else -> Fir{OptionName}Mode.DEFAULT
    }
}

Call in analyzeMetadata() and pass to ValidatedFakeInterface/ValidatedFakeClass constructor.

Layers 9-10 resolve the option and carry the result to code generation. New generation behaviour goes on the FIR path (see CLAUDE.md); the resolver implements the three-tier resolution (annotation → plugin → default) and the shared FakeDeclaration model carries the final boolean that generators consume.

Layer 9 — Resolver + FIR Translator

Files: codegen-runtime/.../compiler/fir/metadata/FirFakeMetadata.kt + compiler/.../fir/generation/FirToFakeDeclarationTranslator.kt + compiler/.../fir/generation/FirFakeEmitter.kt

a) Resolver next to the mirror enum (pattern: resolveCallHistory, resolveMutability):

fun Fir{OptionName}Mode.resolve{OptionName}(pluginDefault: Boolean): Boolean =
    when (this) {
        Fir{OptionName}Mode.{VALUE_A} -> true
        Fir{OptionName}Mode.{VALUE_B} -> false
        Fir{OptionName}Mode.DEFAULT -> pluginDefault
    }

b) In both toFakeInterface() and toFakeClass(), add an enable{OptionName}Default: Boolean parameter and set generate{OptionName} = {optionName}Mode.resolve{OptionName}(enable{OptionName}Default).

c) In both FirFakeEmitter.emit() overloads, pass sharedContext.options.enable{OptionName}Default.

Layer 10 — Shared Model (final boolean)

File: codegen-runtime/.../codegen/analysis/FakeDeclaration.kt

Add the resolved boolean to the sealed interface and override it in both Interface and Class:

val generate{OptionName}: Boolean

The legacy compiler/.../ir/analysis/IrToFakeDeclarationTranslator.kt builds the same model, so it needs the argument to compile until #150 removes it; add no other IR-side logic.

Layer 11 is where the option actually affects generated code — generators read the resolved boolean and conditionally emit different Kotlin source.

Layer 11 — Code Generators (consume the boolean)

Modify generators based on what the option controls:

  • ImplementationGenerator.kt — pass decl.generate{OptionName} to generateCompleteFake()
  • ConfigurationDslGenerator.kt — conditionally generate DSL elements
  • FakeGenerator.kt / extensions — conditional code generation

3. Validate Implementation

Checklist:

  • Enum file created in annotations/ with DEFAULT + two values
  • @Fake annotation has new parameter with DEFAULT default
  • Gradle extension has Property<Boolean> with convention
  • Subplugin passes option via SubpluginOption
  • CLI processor has KEY, OPTION, pluginOptions entry, processOption branch
  • FaktOptions has field + load() reads it
  • FIR mirror enum + fields in both ValidatedFakeInterface and ValidatedFakeClass
  • Both checkers have extract{OptionName}Mode() + call in analyzeMetadata()
  • resolve{OptionName}() next to the mirror enum in FirFakeMetadata.kt
  • toFakeInterface() and toFakeClass() set the resolved boolean; both FirFakeEmitter.emit() overloads pass the plugin default
  • FakeDeclaration (and its Interface/Class) has generate{OptionName}: Boolean
  • Generators consume the boolean
  • make publish-local && make test-sample passes
  • make format applied

4. Three-Tier Resolution Pattern

Every option follows this resolution:

Annotation value (per-interface)
    ↓ if DEFAULT
Plugin setting (build.gradle.kts)
    ↓ if not set
Hardcoded default (in FaktOptions)

This allows: global default via Gradle, per-interface override via annotation, sensible fallback.

Related Skills

  • sample-scaffolder — add a sample that demonstrates the option
  • codegen — understand code generation pipeline for Layer 11

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Runs BDD-compliant GIVEN-WHEN-THEN tests, validates naming compliance, and analyzes coverage. Use when running tests, checking BDD compliance, validating test patterns, or verifying that test names follow GIVEN-WHEN-THEN conventions. Make sure to use this skill whenever tests need to be executed — it validates naming compliance and catches convention violations that plain test runners miss.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rsicarelli/fakt672026년 10월 23일 업데이트

codegen

무료

Documents the Fakt code generation pipeline — immutable AST model, builder DSL, renderer, default value strategies, and extension functions. Use when modifying code generation, changing generated output, adding new generated patterns, understanding builders or renderers, adding a new default value strategy, or debugging generated output. Make sure to use this skill whenever generated Kotlin code needs to change — the pipeline has strict conventions (immutable AST, builder DSL, renderer separation) that must be followed even for small changes.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rsicarelli/fakt672026년 10월 23일 업데이트

commit

무료

Creates project commits — Conventional Commits format, message quality (72 char subject, 80 char body), staged-change checks, and the AI co-author trailer. Use when committing changes, running /commit, creating commit messages, or reviewing commit message quality. Make sure to use this skill whenever a git commit is being created — it is the project's commit workflow and replaces the default one.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rsicarelli/fakt672026년 10월 23일 업데이트

Validates compilation of generated Fakt fakes and diagnoses build failures — where fakes are generated, which path produced them, error classification, and root cause analysis. Use when compilation fails, generated code has errors, no fakes were generated, validating changes compile before committing, or checking if generated code is valid. Make sure to use this skill whenever a build error mentions generated fakes or the Fakt plugin — even if the root cause might be elsewhere, this skill's error classification pinpoints the actual source.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rsicarelli/fakt672026년 10월 23일 업데이트

Creates well-structured GitHub issues using gh CLI with auto-detected project context (version, platform, module). Supports bug reports, feature requests, and internal tasks following repo templates. Use when creating a GitHub issue, reporting a bug, requesting a feature, logging a task, documenting a TODO as an issue, or recording a bug found during development. Make sure to use this skill whenever an issue needs to be created — it auto-detects project context and applies the correct template.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rsicarelli/fakt672026년 10월 23일 업데이트

Checks Kotlin compiler API usage against the Kotlin versions Fakt supports — which path runs on which compiler, how compat shims are written, how to verify across versions, and how to bump Kotlin. Use when calling a new FIR/IR/compiler-plugin API, seeing NoSuchMethodError, NoSuchFieldError or NoClassDefFoundError from compiler classes, bumping the Kotlin version, or checking whether an API exists in the minimum supported Kotlin. Make sure to use this skill whenever compiler plugin code starts using a Kotlin compiler API it didn't use before — these APIs change between Kotlin releases and a break only shows up on an older supported version.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rsicarelli/fakt672026년 10월 23일 업데이트

rsicarelli의 스킬 모두 보기

이 스킬의 문제 신고