Gemini CLI is a terminal coding agent that becomes genuinely useful for Kotlin and Android when you run gemini in the project root and keep a tight GEMINI.md. It reads Gradle files, Kotlin sources, Compose composables, and test directories, then edits and runs commands against the same project state. The highest-value workflow is small, verifiable changes with the agent driving Gradle test and lint tasks after every edit.
What Gemini CLI does well in Kotlin and Android projects
Kotlin and Android work is full of repeatable, well-structured edits: adding a composable, updating a ViewModel, changing a data class, or writing test cases. Gemini CLI suits these tasks because it can parse build.gradle.kts, libs.versions.toml, Android manifest changes, and Kotlin source sets in context. It can follow idiomatic Kotlin conventions such as sealed interfaces for UI state, data classes for models, extension functions for mapping, and coroutine flows for async work. In practice, a single request like ‘add a detail screen with a ViewModel and repository flow’ can produce cohesive changes across Kotlin, Compose, and test files. Gemini CLI can follow multiple files at once and keeps a mental model of the current build configuration. For example, it can add a new destination in a Compose navigation graph, create the screen, update the ViewModel, and add the unit test in one turn. That reduces the ceremony around Android boilerplate while leaving the architecture decisions to the developer. The value is not fully autonomous coding; it is using the agent as a fast, reviewable contributor that already understands Gradle output and Kotlin error messages.
How to set up GEMINI.md for Android builds
Start by placing a GEMINI.md in the project root. This file is the agent’s operating manual; it should list the exact Gradle commands for build, unit test, lint, and instrumented tests, plus the package structure and rules. The more specific it is, the fewer wrong moves the agent makes. Include the module layout, Kotlin source directory (app/src/main/kotlin), test directory, and any version catalog rules. A short but realistic GEMINI.md for a single-module Compose app looks like this:
# GEMINI.md
Project: Kotlin/Android app. Single module `app`. minSdk 26, targetSdk 35.
Build and verify:
- `./gradlew assembleDebug`
- `./gradlew testDebugUnitTest`
- `./gradlew lintDebug`
- `./gradlew connectedDebugAndroidTest` (requires emulator or device)
Folder conventions:
- UI composables: `app/src/main/kotlin/com/example/app/ui/`
- ViewModels: `app/src/main/kotlin/com/example/app/ui/<feature>/`
- Repository/data: `app/src/main/kotlin/com/example/app/data/`
- Unit tests: `app/src/test/kotlin/`
- Instrumented tests: `app/src/androidTest/kotlin/`
Rules:
- Use Kotlin idiomatic code, Jetpack Compose for new UI, and coroutines with `viewModelScope`.
- Do not edit `build/`, `.gradle/`, generated `R` files, or `libs.versions.toml` unless asked.
- Preserve state hoisting and use `collectAsStateWithLifecycle`.
- After changes, run the relevant unit tests and `./gradlew lintDebug`.
- Explain dependency or version changes before editing Gradle files.
Keep this file current. If you add a module or change the test command, update GEMINI.md; the agent reads it on every session. The file should avoid vague guidance like ‘write good code’. Instead, name the module, package, and commands. If you use detekt or ktlint, add those commands too. The agent will quote the file when it is unsure, so the more accurate the instructions, the cleaner the diff. For a full walkthrough of the working style, see How to use Gemini CLI.
The edit-run-review loop in Android projects
Use a plan, small steps, run the tests, review the diff cycle. Start by asking Gemini CLI to inspect the relevant files and propose a plan instead of writing code immediately. Then have it implement one small slice, run ./gradlew testDebugUnitTest and ./gradlew lintDebug, and show the resulting git diff. Review the diff before continuing; if an issue appears, ask for a focused fix rather than a broad rewrite. This keeps Gradle syncs, Compose previews, and coroutine changes reviewable.
Typical prompts that work well for Kotlin and Android tasks:
- In this Android app, add a ViewModel for the search screen that exposes
StateFlow<SearchUiState>and collects repository results withflatMapLatest. Keep the screen state immutable and show a loading indicator. - Refactor the
TaskRepositoryto useFlowinstead of callbacks. UpdateTaskViewModeland unit tests; do not change UI strings or navigation graph. - Write JUnit tests for
LoginViewModelusingkotlinx-coroutines-testand a fakeAuthRepository. Cover loading, success, and error states. - Add a Compose preview for
ProfileScreenwith sample data in theui/profilepackage and run./gradlew compileDebugKotlin. - Fix the lint warning
CoroutineCreationDuringCompositioninHomeScreenby launching the effect inLaunchedEffectrather thanrememberCoroutineScope.
These prompts are specific enough to keep the agent on Kotlin and Android rails and produce a small diff. After each slice, ask for a one-line summary of what changed and which Gradle tasks were run. That makes the review faster and keeps the agent honest about its own verification.
What the agent does well and common Kotlin and Android pitfalls
Gemini CLI handles structured Kotlin edits well: adding data classes, sealed UI state, mapping functions, Compose components, and unit tests. It is also good at reading Gradle task failures and proposing Kotlin fixes. It is less reliable when it changes build configuration, bumps dependency versions, or assumes an emulator is available.
| Area | What to ask for | What to review carefully |
|---|---|---|
| Build files | Add the Compose compiler plugin in the version catalog and keep Kotlin aligned. | AGP, Kotlin, Compose compiler compatibility; configuration avoidance. |
| Compose UI | Create a reusable ListItem composable with state hoisting and a preview. | Experimental Material3 APIs, re-composition scope, remember keys, derivedStateOf. |
| Coroutines | Replace fire-and-forget calls with viewModelScope.launch and expose StateFlow. | Scope cancellation, GlobalScope, collectAsState vs collectAsStateWithLifecycle. |
| Gradle tasks | Run ./gradlew testDebugUnitTest and summarize failures. | Task selection by variant, unit vs instrumented tests, module paths. |
| Tests | Write JUnit tests for the mapper and add edge cases for null input. | Test dispatcher rules, fake data, flaky timing, Robolectric vs Espresso split. |
- Watch Gradle changes: version catalog modifications, AGP/Kotlin/Compose compiler compatibility. Require explicit approval before editing
libs.versions.tomlor plugin blocks. - Compose pitfalls: the agent may add experimental Material3 APIs without opt-in, forget
collectAsStateWithLifecycle, or create recomposition bugs by capturing the wrong scope. - Coroutine pitfalls: it may use
GlobalScopeor launch ininit; correct it toviewModelScopeandstateIn. - Source paths: Android projects often use
src/main/kotlin, but the agent may default tosrc/main/java; verify paths before it writes. - Instrumented tests need a device or emulator. Ask it to write Espresso tests but do not expect
connectedDebugAndroidTestto pass without one. A common failure mode is the agent editing generated build files or trying to run instrumented tests on a machine without an emulator. Spell out inGEMINI.mdwhether the emulator is available and which test tasks are safe.
Running JUnit, lint, and Espresso with Gemini CLI
After each agent change, run ./gradlew testDebugUnitTest and ./gradlew lintDebug from the terminal or let the agent run them and report. For unit tests, use JUnit with kotlinx-coroutines-test and Turbine for flow assertions; ask the agent to include runTest, StandardTestDispatcher, and fake repositories. For Compose UI behavior, generate Espresso tests with createAndroidComposeRule and have the agent add them to app/src/androidTest/kotlin, then run ./gradlew connectedDebugAndroidTest on an emulator. Have the agent read failure reports such as app/build/reports/tests/testDebugUnitTest/index.html and lint reports under app/build/reports/, then propose targeted fixes. Review git diff -- app/src/main/kotlin app/src/test app/src/androidTest before accepting changes. Use a separate worktree or branch when asking for larger refactors so you can throw away a bad attempt without losing the current state. For small fixes, run the tests yourself before asking the agent to continue.
Parallel agents with AgentVera on Android projects
When you need parallel work on a multi-module Android app, AgentVera runs real CLI agents such as Gemini CLI side by side, each in its own git worktree, so one agent editing app never overwrites another editing core. Automatic code review after each turn flags Kotlin or Compose issues at file:line before they land. Checkpoints let you undo a bad refactor of a ViewModel or Gradle file in one click, and token budgets stop an agent from looping on Gradle sync or lint fixes while keeping the conversation for follow-up. For orchestration patterns that fit Kotlin and Android teams, see AI coding agent orchestration.
Final notes for Gemini CLI on Kotlin and Android
Gemini CLI is most productive in Kotlin and Android when you treat it as a focused pair programmer: explicit GEMINI.md, small changes, Gradle-backed verification. For parallel work on multi-module apps, isolate agents and add review checkpoints rather than letting them share a single working tree. Keep the loop tight and the diff small. For more on that partnership, see AI pair programming.