Claude Android Ninja
Drjacky/claude-android-ninja
Build and migrate Android apps with Kotlin, Jetpack Compose, MVVM, Hilt, Room 3 (KSP, SQLiteDriver, Flow/suspend DAOs), Navigation3, and multi-module Gradle.
Sets house defaults for Android and Kotlin Multiplatform work and routes to the right specialized skill for Compose, networking, testing or build tooling.
$ npx skills add rcosteira79/android-skills --skill android-dev -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install rcosteira79/android-skills android-dev --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/rcosteira79/android-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/plugins/android-skills/skills/android-dev .claude/skills/android-dev && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "android-dev" agent skill from https://github.com/rcosteira79/android-skills/tree/main/plugins/android-skills/skills/android-dev into .claude/skills/android-dev/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "android-dev", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/rcosteira79/android-skills/tree/main/plugins/android-skills/skills/android-devType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add rcosteira79/android-skills --skill android-dev -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install rcosteira79/android-skills android-dev --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rcosteira79/android-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/plugins/android-skills/skills/android-dev .agents/skills/android-dev && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "android-dev" agent skill from https://github.com/rcosteira79/android-skills/tree/main/plugins/android-skills/skills/android-dev into .agents/skills/android-dev/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "android-dev", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add rcosteira79/android-skills --skill android-dev -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install rcosteira79/android-skills android-dev --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rcosteira79/android-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/plugins/android-skills/skills/android-dev .cursor/skills/android-dev && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "android-dev" agent skill from https://github.com/rcosteira79/android-skills/tree/main/plugins/android-skills/skills/android-dev into .cursor/skills/android-dev/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "android-dev", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/rcosteira79/android-skills.git --path plugins/android-skills/skills/android-dev--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add rcosteira79/android-skills --skill android-dev -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install rcosteira79/android-skills android-dev --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rcosteira79/android-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/plugins/android-skills/skills/android-dev .gemini/skills/android-dev && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "android-dev" agent skill from https://github.com/rcosteira79/android-skills/tree/main/plugins/android-skills/skills/android-dev into .gemini/skills/android-dev/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "android-dev", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install rcosteira79/android-skills android-devInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add rcosteira79/android-skills --skill android-dev -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/rcosteira79/android-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/plugins/android-skills/skills/android-dev .github/skills/android-dev && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "android-dev" agent skill from https://github.com/rcosteira79/android-skills/tree/main/plugins/android-skills/skills/android-dev into .github/skills/android-dev/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "android-dev", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add rcosteira79/android-skills --skill android-dev -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install rcosteira79/android-skills android-dev --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rcosteira79/android-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/plugins/android-skills/skills/android-dev .opencode/skills/android-dev && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "android-dev" agent skill from https://github.com/rcosteira79/android-skills/tree/main/plugins/android-skills/skills/android-dev into .opencode/skills/android-dev/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "android-dev", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
android-devSets house defaults for Android and Kotlin Multiplatform work and routes to the right specialized skill for Compose, networking, testing or build tooling.
Defaults cover dependency injection with Hilt plus KSP, or Koin when a project already uses it, coroutines and Flow instead of LiveData in new code, kotlinx.serialization for JSON, and Coil for images. Android uses Retrofit and OkHttp with Room while shared KMP code uses Ktor with Room or SQLDelight, and Retrofit never goes in a shared module since it is Android-only; module boundaries keep the core model package free of Android dependencies and stop feature modules from depending on each other, with platform exceptions mapped to a domain type at the repository boundary.
A routing table sends more specific work to sibling skills, covering Compose detail, Material 3 UX audits, coroutines and Flow operators, the data layer and error model, Retrofit versus Ktor networking, paging, image loading, typed local storage, KMP expect and actual boundaries, RxJava migration, testing, Koin, Gradle build logic and speed, Logcat debugging, AOSP source lookup and modularization, always loaded with the full android-skills prefix rather than a short name.
Read from SKILL.md and the folder at commit 0f9af5a. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are kotlin).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Android and KMP Baseline loads about 3k tokens when it runs. Until then it costs about 221 tokens; SKILL.md has 1,039 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from rcosteira79/android-skills at commit 0f9af5a, republished under its MIT licence (© rcosteira79). 1,039 words, ~2,998 tokens.
.claude/skills/android-dev/SKILL.md (or your agent's skills folder).House defaults — apply them without reminders or re-derivation; where the project's actual conventions differ, follow the project:
android-skills:koin when the project uses Koin). Async: Coroutines/Flow — no LiveData in new code. JSON: kotlinx.serialization. Images: Coil.:core:model has zero Android deps; :feature:* modules never depend on each other.android-skills:android-data-layer).Load the specific skill for the task, always with the fully-qualified android-skills: prefix — never the short name (compose, koin, …).
| For | Load |
|---|---|
Compose detail — stability, remember, modifiers, side effects, lists, animation, navigation | android-skills:compose |
| M3 UX — touch targets, adaptive/foldable layouts, accessibility & M3-compliance audit | android-skills:android-ux |
Coroutines & Flow — operators, Channel vs SharedFlow, structured concurrency | android-skills:kotlin-coroutines, android-skills:kotlin-flows |
| Repository / data layer + error model | android-skills:android-data-layer |
| Networking | android-skills:android-retrofit (Android) · android-skills:kmp-ktor (KMP) |
| Paging | android-skills:paging |
| Image loading | android-skills:coil-compose |
| Preferences / typed local storage | android-skills:datastore |
KMP expect/actual boundary design | android-skills:kmp-boundaries |
| RxJava → Coroutines/Flow migration | android-skills:rxjava-migration |
| Testing | android-skills:android-testing |
| DI with Koin | android-skills:koin |
| Build logic / convention plugins | android-skills:android-gradle-logic |
| Build speed, kapt → KSP | android-skills:gradle-build-performance |
| Debugging — Logcat, crashes, ANRs, profiling | android-skills:android-debugging |
| AOSP / AndroidX source lookup | android-skills:android-source-search |
| Multi-module visibility & module boundaries | android-skills:modularization |
| Platform PDF annotation / page-object editing (API 36.1 / SDK ext 18) | android-skills:pdf-annotations |
For a new project or feature with no established convention. In existing code, match what's already there — see Reuse the project's existing mechanism below.
The UI layer is MVVM with an MVI-style state/effect split:
UiState per screen, exposed as StateFlow<UiState> and structured with the four buckets below. The content composable renders it and emits callbacks — nothing else.Channel(Channel.BUFFERED).receiveAsFlow(), not SharedFlow: an effect emitted while the screen is backgrounded buffers and replays on resume instead of being dropped. Collect it in a LaunchedEffect (lifecycle-scoped via repeatOnLifecycle), never collectAsStateWithLifecycle. Channel-vs-SharedFlow rationale: android-skills:kotlin-flows.UiState); anything the UI runs once and forgets is an effect. This is the durable-state-over-events rule in compose/references/state-management.md: keep durable things in state; the effect stream is only for one-shot imperatives.@Stable Actions interface at ~4–5+ (or when the same set is threaded through several composable layers). Below that, individual lambdas are simpler — don't abstract early. The ViewModel implements the interface; the content composable depends on FooActions, never the ViewModel, so it stays pure and previewable (a no-op object : FooActions {} in previews).data class FooUiState(/* the four buckets — see below */)
sealed interface FooEffect {
data class NavigateTo(val id: String) : FooEffect
data class ShowSnackbar(val message: String) : FooEffect
}
@Stable // promote here once lambdas pile up (~4-5+)
interface FooActions {
fun onItemClick(id: String)
fun onRefresh()
}
class FooViewModel(/* … */) : ViewModel(), FooActions {
private val _uiState = MutableStateFlow(FooUiState())
val uiState: StateFlow<FooUiState> = _uiState.asStateFlow()
private val _effects = Channel<FooEffect>(Channel.BUFFERED) // not SharedFlow — buffers while backgrounded
val effects = _effects.receiveAsFlow()
override fun onItemClick(id: String) { /* _uiState.update { … } */ _effects.trySend(FooEffect.NavigateTo(id)) }
override fun onRefresh() { /* … */ }
}
@Composable
fun FooScreen(viewModel: FooViewModel = hiltViewModel(), onNavigate: (String) -> Unit) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val lifecycle = LocalLifecycleOwner.current.lifecycle
LaunchedEffect(Unit) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.effects.collect { effect ->
when (effect) {
is FooEffect.NavigateTo -> onNavigate(effect.id)
is FooEffect.ShowSnackbar -> { /* show snackbar */ }
}
}
}
}
FooContent(uiState = uiState, actions = viewModel) // VM passed as FooActions — FooContent sees only the interface
}Kotlin 2.4+: collapse the
_uiState/uiStatepair with explicit backing fields (val uiState: StateFlow<FooUiState>+field = MutableStateFlow(…)) —uiStateonly, never the effectsChannel. Full idiom + version gate:android-skills:kotlin-flows.
Screens with rich interactions (forms, calculators, multi-step wizards) get unmanageable when state is one flat data class. Slice UiState into four explicit buckets, and derive computed values as class properties, not constructor parameters:
data class CheckoutUiState(
// 1. Editable input — what the user types
val email: String = "",
val cardNumber: String = "",
// 3. Persisted snapshot — last value read from the repository / stored cross-screen
val savedShippingAddress: Address? = null,
// 4. Transient UI-only — flags that must NOT survive the screen
val isSubmitting: Boolean = false,
val showCardScannerOverlay: Boolean = false,
) {
// 2. Derived — getters, NOT constructor params, so no caller can copy() into an
// inconsistent state (e.g. emailValid = false next to a valid email).
val emailValid: Boolean get() = email.isValidEmail()
val canSubmit: Boolean get() = emailValid && cardNumber.passesLuhn() && !isSubmitting
}The bucket dictates lifecycle and persistence, not the field. Persisting isSubmitting keeps the spinner forever after process death; computing canSubmit outside the class lets it drift from the inputs; persisting cardNumber cross-screen leaks PII. Mixing the buckets produces bugs that look architectural.
UiState is the screen's contract with its ViewModel, not a UI model — it gets its own file (or the ViewModel's, if that's the project's layout). Never sweep UI models into it. "Each type has a single owner / it's one unit of change / the real seam is role-per-file" argues for exactly this mistake: the state and the models it holds are different roles.
Group the models by composition, never by screen:
ChallengeDetailUi.kt holds ChallengeDetailUi plus the ChallengeTaskUi / SponsorshipUi it is built from.FooModels.kt / FooUiModels.kt is never the answer — reaching for a grab-bag name proves no aggregate root was found, which means the types are independent and belong in their own files.Kotlin's "related declarations may share a file named after the primary declaration" is the mechanism, not a licence to nominate the state as that primary declaration and sweep the models in behind it.
Before adding any new mechanism — an event dispatcher, an effects Channel/SharedFlow, a use-case layer, or a parallel state field — open a sibling ViewModel in the same feature and reuse what's already there. The easy miss here is duplicating an existing mechanism instead of widening it — adding a second shouldDisplayUndoX flag beside the existing one rather than generalizing the one that's there. If existing code contradicts a "best practice," follow the code and flag the inconsistency; never silently override the project's architecture.
The test for every comment: could a reader quickly infer what it says from the code beside it? If yes, it's redundant — delete it. A comment survives only by carrying what the code cannot: a non-obvious why — a decision, constraint, workaround, or gotcha. Never narrate what the code does; clear names and small functions already say it. "What a well-known type or call does" is a what the reader can look up, not a why.
Write the fewest comments that pass that test — this holds even when a task says "make it readable" or "for juniors." Readability comes from naming and structure; a comment a newcomer needs in order to follow what the code does is a signal to rename or extract, not to annotate.
Keep a genuine why (// rethrow first — a broad catch would swallow CancellationException), a justifying comment at a surprising call site, KDoc on a public API that adds information beyond its signature, and TODO(owner-or-link). Honor an explicit request for documentation.
Delete on sight — each is trivially inferable from the code beside it:
// ---- domain model ---- // section-divider / banner (any width)
/** The user profile as the app cares about it. */ // KDoc restating the class name
val uiState = _uiState.asStateFlow() // private mutable, public read-only (restates the idiom)
} catch (e: IOException) { // no connectivity, timeout, DNS failure (restates what the type means)Inject a CoroutineDispatcher everywhere rather than calling Dispatchers.Main / Dispatchers.IO directly: Dispatchers.Main isn't guaranteed on every KMP target without the -ktx artifacts, and injection is also what makes dispatcher-swapped tests possible. Use expect/actual for platform specifics (file I/O, push tokens, biometrics); on iOS prefer immutable shared state.
© rcosteira79, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in plugins/android-skills/skills/android-dev of rcosteira79/android-skills.
Open the folder on GitHubat commit 0f9af5a
Android and KMP Baseline next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Android and KMP Baseline this skillrcosteira79/android-skills | 150 | — | ~3k | Automated safety check: Pass | MIT | |
| Claude Android NinjaDrjacky/claude-android-ninja | 124 | — | ~5.2k | Automated safety check: Pass | Apache-2.0 | |
| Expo Brownfield Integrationmweinbach/agent-coworker | 156 | 2 repos | ~900 | Automated safety check: Notes | Custom licence | |
| Android Audio E2Ehyochan/react-native-nitro-sound | 961 | — | ~814 | Automated safety check: Pass | MIT | |
| Upgrade React NativeShopify/flash-list | 7.2k | — | ~1.9k | Automated safety check: Pass | MIT | |
| Kotlin Tooling Agp9 MigrationJetBrains/skills | 363 | 2 repos | ~5.6k | Automated safety check: Pass | Apache-2.0 |
Drjacky/claude-android-ninja
Build and migrate Android apps with Kotlin, Jetpack Compose, MVVM, Hilt, Room 3 (KSP, SQLiteDriver, Flow/suspend DAOs), Navigation3, and multi-module Gradle.
mweinbach/agent-coworker
Helps add Expo and React Native to an existing native iOS or Android app, and choose between a prebuilt AAR or XCFramework and a fully integrated build.
hyochan/react-native-nitro-sound
Build and run device-backed Android end-to-end checks for react-native-nitro-sound permissions, recording, playback, listeners, pause/resume, seek, speed, audio focus, lifecycle behavior, and rapid…
Shopify/flash-list
Upgrade the React Native fixture app to a new version. An agent skill from Shopify/flash-list.
JetBrains/skills
Migrates Kotlin Multiplatform (KMP) projects to Android Gradle Plugin 9.0+.
arindamxd/camerax-android
Upgrades, or migrates, an Android project to use Android Gradle Plugin (AGP) version 9.
rcosteira79/android-skills
Guides writing correct, performant Compose and Compose Multiplatform UI across Android, desktop, iOS and web, from state and animation to design-to-code and crash fixes.
rcosteira79/android-skills
Helps choose how common Kotlin Multiplatform code reaches platform APIs: expect/actual, a common interface with platform bindings, or separate platform screens.
rcosteira79/android-skills
Sets out the repository pattern's error-propagation model for Android and Kotlin Multiplatform, plus the Room setup for commonMain.
rcosteira79/android-skills
Android and KMP debugging techniques for crashes, ANRs, memory leaks, R8 traces, Gradle failures and Compose recomposition, built on finding the root cause first.
rcosteira79/android-skills
Sets up Android Gradle convention plugins in a build-logic composite build, including the version catalog wiring and shared configuration that are easy to get wrong.
rcosteira79/android-skills
A skill your agent uses when setting up or working with Retrofit in Android — service interface definitions, coroutines integration, OkHttp configuration, Hilt module setup, and error handling in…
Categories
Sets house defaults for Android and Kotlin Multiplatform work and routes to the right specialized skill for Compose, networking, testing or build tooling. serialization for JSON, and Coil for images. Android uses Retrofit and OkHttp with Room while shared KMP code uses Ktor with Room or SQLDelight, and Retrofit never goes in a shared module since it is Android-only; module boundaries keep the core model package free of Android dependencies and stop feature modules from depending on each other, with platform exceptions mapped to a domain type at the repository boundary.
Android and KMP Baseline fits situations like: starting any Android or Kotlin Multiplatform task; fixing a bug or refactoring a ViewModel in an Android app; deciding which specialized Android skill a task should route to.
Run `npx skills add rcosteira79/android-skills --skill android-dev -a claude-code`. Or copy the skill folder (plugins/android-skills/skills/android-dev in rcosteira79/android-skills) into .claude/skills/android-dev in your project. Claude Code loads it when a task matches its description.
Run `npx skills add rcosteira79/android-skills --skill android-dev -a codex`. Or copy the skill folder (plugins/android-skills/skills/android-dev in rcosteira79/android-skills) into .agents/skills/android-dev in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add rcosteira79/android-skills --skill android-dev -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/android-dev, .gemini/skills/android-dev, .github/skills/android-dev and .opencode/skills/android-dev in your project.
SKILL.md names no scripts, command-line tools or credentials: Android and KMP Baseline is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Android and KMP Baseline is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 3k tokens (SKILL.md is roughly 12k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Android and KMP Baseline: Claude Android Ninja (Drjacky/claude-android-ninja, 124 stars), Expo Brownfield Integration (mweinbach/agent-coworker, 156 stars), Android Audio E2E (hyochan/react-native-nitro-sound, 961 stars) and Upgrade React Native (Shopify/flash-list, 7.2k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
rcosteira79 (a GitHub user) maintains it in rcosteira79/android-skills, which has 150 GitHub stars. The repository holds 21 skills in this directory. The repository was last updated on September 19, 2026.
Source: rcosteira79/android-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.