Android client for Pyrycode. Native Kotlin + Jetpack Compose.
Phase 0 — UI scaffolding. Built against a fake data layer (FakeConversationRepository); real backend integration is deferred to Phase 4 (after pyrycode ships its mobile API in Phase 3).
The data-model entity is Conversation with an isPromoted: Boolean flag, surfaced as two UI tiers:
- Discussions (unpromoted) — auto-named, throwaway, scratch cwd, 30-day auto-archive.
- Channels (promoted) — user-named, persistent, dedicated cwd by default (or bound to existing project folder), eligible for memory plugins. The main list.
Sessions are nested under conversations. A conversation has a current active session and history of past sessions. Threads render messages chronologically across sessions, with delimiters at session boundaries (/clear, idle-evict, workspace change). Above-delimiter messages are visually de-emphasized; the explanatory line below the delimiter reminds users that claude doesn't remember above the line and offers a memory-plugin install affordance.
Conversation is server-side (defined by pyrycode CLI's eventual conversations.json registry). Discord-side (Phase 2 of pyrycode) and Mobile-side (Phase 3 Mobile API) consume the same entity.
- Kotlin + Jetpack Compose + Material 3
- Min SDK 33 (Android 13), target SDK latest stable
- Architecture: MVI with
ViewModel+StateFlow; sealedUiState+Eventtypes - Koin (DI), Ktor (networking, Phase 4+), DataStore (settings)
- Single Gradle module
- Gradle Kotlin DSL +
gradle/libs.versions.tomlversion catalog
./gradlew assembleDebug # build debug APK
./gradlew installDebug # install on connected device/emulator
./gradlew test # unit tests
./gradlew connectedAndroidTest # instrumented tests (device required)
./gradlew lint # Android Lint
./gradlew clean # clean build outputsSingle module app/. Source root: app/src/main/java/de/pyryco/mobile/. Modularize when build incremental > 60s or screens > 10.
Planned package layout (Phase 1+):
de/pyryco/mobile/
├── MainActivity.kt
├── PyryApp.kt # Composition root, Koin setup
├── ui/
│ ├── theme/ # Material 3 theme + typography
│ ├── conversations/
│ │ ├── list/ # Channel list + recent-discussions drilldown
│ │ ├── thread/ # Conversation thread (channels + discussions)
│ │ └── components/ # ConversationRow, Delimiter, WorkspaceChip,
│ │ # PromotionDialog, WorkspacePicker
│ └── settings/
├── data/
│ ├── model/ # Conversation, Session, Message
│ ├── repository/ # ConversationRepository interface + Fake/Remote impls
│ └── network/ # Ktor client (Phase 4)
└── di/ # Koin modules
ConversationRepository.observeMessages(conversationId) paginates across past sessions transparently, producing a chronological stream interleaved with synthetic SessionBoundary markers that the thread screen renders as horizontal-rule delimiters.
- Test-first. Red → green → refactor. Failing test first, implementation after.
- Stateless composables. Hoist state to the ViewModel; UI receives
state: UiStateandonEvent: (Event) -> Unit. - Sealed types for
UiStateandEvent. - No direct push to
main. PR + review. - Bundle ID
de.pyryco.mobile(locked at first Play Store publish — do not rename without understanding the cost).
- Don't add a real backend client until Phase 4 (after pyrycode's mobile API ships).
- Don't bake Android-only assumptions into the data layer — Compose Multiplatform is a walk-back trigger; keep
data/portable. - Don't generalize an agent dispatcher across pyrycode + pyrycode-mobile prematurely. The eventual
pyrycode-mobile-agentsrepo is a fork, not a reuse. - Don't refactor adjacent code "while you're there." Touch only what's necessary.