Compose feature scaffold
Skill esaldgut/ai-native-engineering-workspace/global-skills/android/compose-feature-scaffold
Bootstrap a complete Compose feature module at once the canonical Android way — domain model + repository interface + RepositoryImpl + DTO mapper + screen-level @HiltViewModel + @Composable Screen + a Hilt @Module that @Binds the interface + a Navigation Compose entry. Wires @HiltAndroidApp, @AndroidEntryPoint on a single MainActivity, @Binds over @Provides for the interface, the Hilt 1.3.0 hilt-lifecycle-viewmodel-compose artifact, and type-safe @Serializable routes. Use when starting a new feature; for a single file in an existing feature use compose-clean-architecture-module-scaffold.From its SKILL.md
npx -y skills add esaldgut/ai-native-engineering-workspace --skill compose-feature-scaffoldAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.6 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it
Compose feature scaffold (Kotlin, Hilt + Navigation)
This is compose-clean-architecture-module-scaffold promoted from "one file" to "one feature": it
generates the full UI / Domain / Data stack plus the Hilt module and the Navigation Compose entry,
so a new feature module is consistent and wired from the first commit. It composes the same official
guidance and adds the two pieces a single file doesn't need — DI binding and a nav destination.
When to invoke
- You're starting a new feature (Feed, Profile, Checkout) and want the layered tree, the
@HiltViewModel, the@Bindsmodule, and theNavHostentry created together. - You're standing up the app shell:
@HiltAndroidApp Application, the single@AndroidEntryPoint MainActivity, and the rootNavHost.
For adding one type to a feature that already exists, use
compose-clean-architecture-module-scaffold instead.
Announce on invoke: "Using compose-feature-scaffold to generate the full Compose feature
(layers + Hilt @Binds module + Navigation entry) per Android's Hilt and Navigation guidance."
Files this scaffold produces
feature/myfeature/
├── domain/model/MyItem.kt # immutable data class
├── domain/repository/MyRepository.kt # interface
├── data/api/MyApi.kt + MyItemDto.kt # DTO + service
├── data/mapper/MyItemMapper.kt # DTO -> domain
├── data/repository/MyRepositoryImpl.kt # @Inject constructor, implements interface
├── di/MyFeatureModule.kt # @Module @InstallIn, @Binds interface->impl
├── ui/MyUiState.kt # sealed interface
├── ui/MyFeatureViewModel.kt # @HiltViewModel, screen-level
├── ui/MyFeatureScreen.kt # @Composable, collectAsStateWithLifecycle
└── ui/MyFeatureNav.kt # @Serializable route + composable() entry
The wiring rules (load-bearing)
@Binds, not@Provides, for interface → impl. Both compile, but@Bindsgenerates a zero-overhead direct cast in the factory;@Providesfor a single-impl interface is a review smell.- Exactly one
@AndroidEntryPointActivity. Single-Activity is the canonical Compose pattern; the oneMainActivitycarries@AndroidEntryPointand@HiltAndroidAppgoes on theApplication. Annotating multiple Activities implies the multi-Activity anti-pattern Compose was built to remove. hiltViewModel()artifact follows nav scoping (Hilt 1.3.0). Host-scoped →androidx.hilt:hilt-lifecycle-viewmodel-compose. Nav-back-stack-scoped →androidx.hilt:hilt-navigation-compose+hiltViewModel(navBackStackEntry). The wrong choice gives "my state vanished" or "my state leaked across screens" bugs.- KSP, not KAPT, for Hilt. The feature module's
build.gradle.ktsappliesid("com.google.dagger.hilt.android")+id("com.google.devtools.ksp"). KAPT is being phased out and is markedly slower. - Type-safe
@Serializableroutes, no inline strings. Declare a@Serializableroute object/class per destination and reference it at theNavHostcallsite — never a hardcoded"myfeature"string — so the call graph is greppable and the route is a compile-time symbol.
Canonical example
// ---------- di/MyFeatureModule.kt ----------
@Module
@InstallIn(SingletonComponent::class)
abstract class MyFeatureModule {
@Binds abstract fun bindRepo(impl: MyRepositoryImpl): MyRepository
}
// ---------- ui/MyFeatureViewModel.kt (see clean-architecture skill for the body) ----------
@HiltViewModel
class MyFeatureViewModel @Inject constructor(
private val repo: MyRepository,
) : ViewModel() {
val uiState: StateFlow<MyUiState> =
flow { emit(repo.fetchItem("42")) }
.map<MyItem, MyUiState> { MyUiState.Loaded(it) }
.catch { emit(MyUiState.Error(it.message.orEmpty())) }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), MyUiState.Loading)
}
// ---------- ui/MyFeatureScreen.kt ----------
@Composable
fun MyFeatureScreen(viewModel: MyFeatureViewModel = hiltViewModel()) {
val state by viewModel.uiState.collectAsStateWithLifecycle()
// render state...
}
// ---------- ui/MyFeatureNav.kt — type-safe route ----------
@Serializable data object MyFeatureRoute
fun NavGraphBuilder.myFeatureGraph() {
composable<MyFeatureRoute> { MyFeatureScreen() }
}
// ---------- App shell ----------
@HiltAndroidApp class MyApp : Application()
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val nav = rememberNavController()
NavHost(nav, startDestination = MyFeatureRoute) { myFeatureGraph() }
}
}
}
Decision aids
- Gradle module (
:feature:myfeature) vs package in:app? Multi-module is canonical for medium+ codebases (faster incremental builds, clear ownership); a package is fine for a small app. Default to a Gradle module once the app has more than a handful of features. - Type-safe vs string routes? Type-safe
@Serializable(shown above) is the modern recommendation (navigation-compose 2.8+); the string formcomposable("myfeature")still works if you're on an older setup. Prefer type-safe for new code. @Bindsmodule abstract class vs@Providesobject? Use anabstract class+@Bindsfor interface→impl; use anobject+@Providesonly for types you don't own (e.g. constructing a Retrofit or KtorHttpClient).
Related skills
global-skills/android/compose-clean-architecture-module-scaffold/SKILL.md— per-file version and the full layer-rule rationale (ViewModel,UiState, mapper).global-skills/android/android-testing-patterns/SKILL.md— the per-layer test suite for the files this scaffold emits.global-skills/android/android-security-checklist/SKILL.md— harden the new feature's network + manifest surface before shipping.
Sources
- Hilt for Android · Hilt in multi-module apps · Hilt with other Jetpack libraries
- Navigation with Compose · Architecture recommendations
Last verified: 2026-06-03 against developer.android.com (Hilt multi-module @Binds, Hilt 1.3.0
artifact, Navigation Compose type-safe routes) + Compose BOM 2026.05.00.
Re-check after: Google I/O 2026 + next Compose BOM major, or by 2026-12-01. Decay risk: high
(Hilt artifacts + Navigation Compose APIs evolve).
Found a drift? Run /skill-pattern-freshness-audit android.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.