-
Notifications
You must be signed in to change notification settings - Fork 0
Skills
Skills activate automatically based on context—no manual invocation needed.
Auto-activates when: User mentions "create feature", "new module", "add feature"
Complete Feature Generation Workflow:
Phase 0: Context Discovery (AUTO)
├── Detect PKG_PREFIX from feature/*/build.gradle.kts
├── Find initKoin.kt path (contains startKoin)
├── Find BaseAppNavHost.kt path (contains NavHost)
└── Detect core module packages
Phase 1: PRD Generation
├── Analyze user prompt
├── Generate Product Requirements Document
├── Save to .claude/docs/{feature}/prd.txt
└── ⏸️ Wait for user approval
Phase 2: Task Generation
├── Break PRD into implementation tasks
├── Assign tasks to agents (data, ui, integration)
├── Save to .claude/docs/{feature}/tasks.md
└── ⏸️ Wait for user approval
Phase 3: Implementation (Parallel)
├── 🔧 data-layer-agent (runs in parallel)
│ ├── Create models with @Serializable
│ ├── Create Ktor Resources (type-safe routes)
│ ├── Implement RemoteDataSource (interface + impl)
│ ├── Implement Repository (interface + impl)
│ └── Validate build
│
├── 🎨 ui-layer-agent (runs in parallel)
│ ├── Create UiModel (presentation models)
│ ├── Implement ViewModel with 4-state pattern
│ ├── Create Composable Screens with X-components
│ ├── Setup Navigation with type-safe routes
│ └── Validate build
│
└── 🔗 integration-agent (runs after data + ui)
├── Create DI module with Koin
├── Add to settings.gradle.kts
├── Add dependency to composeApp
├── Register in initKoin.kt
├── Wire navigation in BaseAppNavHost.kt
├── Generate living specification
└── Validate full build
Phase 4: Cleanup
├── Verify spec.md exists
├── Remove prd.txt (ephemeral)
├── Remove tasks.md (ephemeral)
└── Remove task-*.md files (ephemeral)
Examples:
Simple Feature (UI-only, no API):
> Create settings feature with toggle switches for notifications and dark modeGenerates: ViewModel, Screen, Navigation (no DataSource/Repository)
Complex Feature (API + multiple screens):
> Create product catalog with list, search, filters, and detail screensGenerates: Models, Ktor Resources, DataSource, Repository, UiModels, ViewModels, Screens, Navigation
Feature with Authentication:
> Create login feature with email/password and OAuth supportGenerates: LoginRequest/Response models, AuthRepository, LoginViewModel, LoginScreen, token storage
What Gets Generated:
Data Layer:
// Models
@Serializable
data class Product(
val id: String,
val name: String,
val price: Double,
val imageUrl: String?
)
// Ktor Resources
@Resource("/products")
class ProductResource {
@Resource("{id}")
data class Id(val parent: ProductResource, val id: String)
}
// DataSource
interface ProductRemoteDataSource {
suspend fun getProducts(): Either<List<Product>>
suspend fun getProduct(id: String): Either<Product>
}
class ProductRemoteDataSourceImpl(
private val client: ApiClient
) : ProductRemoteDataSource {
override suspend fun getProducts(): Either<List<Product>> =
client.get(ProductResource())
}
// Repository
interface ProductRepository {
suspend fun getProducts(): Either<List<Product>>
suspend fun getProduct(id: String): Either<Product>
}
class ProductRepositoryImpl(
private val dataSource: ProductRemoteDataSource
) : ProductRepository {
override suspend fun getProducts(): Either<List<Product>> =
dataSource.getProducts()
}UI Layer:
// UiModel
data class ProductUiModel(
val id: String,
val name: String,
val price: String, // Formatted: "$99.99"
val imageUrl: String?
)
// ViewModel
class ProductListViewModel(
private val repository: ProductRepository
) : ViewModel() {
private val _state = MutableStateFlow<ProductListUiState>(Uninitialized)
val state: StateFlow<ProductListUiState> = _state.asStateFlow()
fun loadProducts() {
viewModelScope.launch {
setState { Loading }
when (val result = repository.getProducts()) {
is Success -> setState {
Success(result.data.map { it.toUiModel() })
}
is Failure -> setState { Failed(result.error) }
}
}
}
}
// Screen
@Composable
fun ProductListScreen(
viewModel: ProductListViewModel = koinViewModel(),
onProductClick: (String) -> Unit
) {
val state by viewModel.state.collectAsState()
XScaffold(
title = { XText("Products") }
) {
when (val currentState = state) {
is Uninitialized -> LaunchedEffect(Unit) {
viewModel.loadProducts()
}
is Loading -> XLoadingIndicator()
is Success -> ProductList(
products = currentState.products,
onProductClick = onProductClick
)
is Failed -> XErrorView(error = currentState.error)
}
}
}
// Navigation
@Serializable
data class ProductListRoute(
val onProductClick: (String) -> Unit
)Integration:
// DI Module
class ProductModule : BaseFeature {
override fun Module.install() {
single<ProductRepository> { ProductRepositoryImpl(get()) }
single<ProductRemoteDataSource> { ProductRemoteDataSourceImpl(get()) }
viewModel { ProductListViewModel(get()) }
}
}
// initKoin.kt (auto-registered)
modules(ProductModule().module)
// BaseAppNavHost.kt (auto-wired)
composable<ProductListRoute> {
ProductListScreen(
onProductClick = { id -> navController.navigate(ProductDetailRoute(id)) }
)
}Auto-activates when: User mentions "change feature", "modify feature", "update feature", "add to feature"
Spec-First Modification Workflow:
Phase 0: Context Discovery (AUTO)
└── Detect project structure
Phase 1: Load Specification
├── Check for .claude/docs/{feature}/spec/*.md
├── If missing: Generate spec using /generate-spec
└── Load spec into context
Phase 2: Understand Current Implementation
├── Parse spec for architecture patterns
├── Identify data models, API contracts
├── Understand state management
└── Map navigation structure
Phase 3: Plan Changes
├── Determine affected layers (data/ui/integration)
├── Load relevant architecture references
├── Plan implementation approach
└── Identify files to modify
Phase 4: Implement Changes
├── Apply changes following established patterns
├── Maintain consistency with existing code
└── Follow 10 critical rules
Phase 5: Validate
├── Run incremental build
├── Run ktlintFormat
└── Fix any errors
Phase 6: Update Specification
├── Regenerate spec from implementation
├── Add changelog entry at top
└── Preserve previous changelog entries
Examples:
Add Functionality:
> Add sorting by price and date to the product list featureChanges: ProductListViewModel (add sort state), ProductListScreen (add sort UI)
Refactor:
> Refactor the login screen to use a stepper for multi-step authenticationChanges: LoginUiState (add step state), LoginScreen (add stepper UI)
Fix Issues:
> Fix the loading state not showing in the profile featureChanges: ProfileViewModel (fix state transition), ProfileScreen (verify loading UI)
Add New Screen:
> Add a forgot password screen to the login featureChanges: Add ForgotPasswordScreen, update navigation, add ViewModel
Spec Changelog Entry:
## Last Updated
2025-01-05 - Added sorting by price and date
- ProductListUiState: Added sortBy field
- ProductListViewModel: Added setSortOrder function
- ProductListScreen: Added sort dropdown menu
2025-01-03 - Initial implementation
- Created product list with search and filtersAuto-activates when: Working in feature/*/ui/ directories, creating Composables, mentions "UI", "screen", "component"
Design System Enforcement:
Ensures X-components are used instead of Material3, preventing design drift.
Component Mappings:
// ❌ Material3 // ✅ X-Components
Scaffold → XScaffold
Button → XButton
OutlinedButton → XOutlinedButton
TextButton → XTextButton
Text → XText
TextField → XTextField
OutlinedTextField → XOutlinedTextField
Card → XCard
Icon → XIcon
IconButton → XIconButton
Switch → XSwitch
Checkbox → XCheckbox
RadioButton → XRadioButton
Divider → XDivider
CircularProgressIndicator → XLoadingIndicatorExamples:
Creating a Login Form:
> Create a login form with email, password, and submit buttonAI generates:
@Composable
fun LoginForm(
email: String,
password: String,
onEmailChange: (String) -> Unit,
onPasswordChange: (String) -> Unit,
onSubmit: () -> Unit
) {
Column {
XTextField(
value = email,
onValueChange = onEmailChange,
label = { XText("Email") }
)
XTextField(
value = password,
onValueChange = onPasswordChange,
label = { XText("Password") },
visualTransformation = PasswordVisualTransformation()
)
XButton(
onClick = onSubmit,
text = "Login"
)
}
}Creating a Product Card:
> Create a product card component with image, title, price, and add to cart buttonAI generates using XCard, XText, XButton (not Material3 components)
Theme Usage:
// ✅ Correct
XTheme {
XScaffold { }
}
// ❌ Wrong
MaterialTheme {
Scaffold { }
}Auto-activates when: User mentions "Swift bridge", "iOS integration", "native SDK"
Swift-to-Kotlin Integration Patterns:
Uses interface injection to integrate iOS-specific SDKs while maintaining Clean Architecture.
Pattern:
1. Define Kotlin interface (commonMain)
2. Implement in Kotlin (androidMain) - usually stub
3. Implement in Swift (iosMain via expect/actual)
4. Inject via Koin DI
Examples:
Biometric Authentication:
> Integrate iOS Face ID authentication into the login featureGenerates:
// commonMain - Interface
interface BiometricAuthenticator {
suspend fun authenticate(reason: String): Either<Boolean>
}
// androidMain - Android implementation
actual class BiometricAuthenticatorImpl : BiometricAuthenticator {
override suspend fun authenticate(reason: String): Either<Boolean> {
// Android BiometricPrompt implementation
}
}
// iosMain - expect/actual bridge
expect class BiometricAuthenticatorImpl() : BiometricAuthenticator
// iosMain Swift bridge
@Composable
actual fun rememberBiometricAuthenticator(): BiometricAuthenticator {
return remember { BiometricAuthenticatorSwiftBridge() }
}// Swift implementation
class BiometricAuthenticatorSwiftBridge: BiometricAuthenticator {
func authenticate(reason: String) async -> Either<Bool> {
let context = LAContext()
// Face ID implementation
}
}Camera Integration:
> Add iOS camera capture to the profile photo featurePayment Processing:
> Integrate Apple Pay into the checkout featurePush Notifications:
> Add iOS push notification handling to the messaging feature