* chore(agent): Add mandatory rules for AI agents covering Git, i18n, and formatting * Explicit path for strings resource Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Specify both strings.xml & plurals.xml Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Also exclude non-base local folders from i18n-kmk Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
218 lines
11 KiB
Markdown
218 lines
11 KiB
Markdown
# Komikku – AI Agent Guide
|
||
|
||
Komikku is an Android manga reader (min SDK 26, target SDK 36, JVM 17 / Kotlin) forked from **Mihon** + **TachiyomiSY**. Stack: Jetpack Compose + Material3, Voyager navigation, SQLDelight, Injekt DI. `applicationId`: `app.komikku`.
|
||
|
||
---
|
||
|
||
## Mandatory rules for AI agents
|
||
|
||
**Read this section before every change.** These rules override shortcuts (e.g. copying nearby `MR` imports or only running `compileDebugKotlin`).
|
||
|
||
### Git
|
||
|
||
| Rule | Required behavior |
|
||
|------|-------------------|
|
||
| Branch | Create a **feature branch** for the task (`git checkout -b <type>/<short-description>`). |
|
||
| Commit | **OK** on a feature branch when work is ready. **Never** commit directly to `master` / `main` unless the user explicitly asks. |
|
||
| Push | **OK** to push the **current feature branch** when work is ready. **Never** push to `master` / `main` unless the user explicitly asks. |
|
||
|
||
Before `git push`, confirm the current branch is not `master` or `main` (`git branch --show-current`).
|
||
|
||
### Internationalization (strings)
|
||
|
||
| String kind | Module | Resource class | Base folder only |
|
||
|-------------|--------|----------------|------------------|
|
||
| Komikku-only (new features, KMK UI, library-update errors, WebDAV, Discord, etc.) | `i18n-kmk/` | **`KMR`** | `i18n-kmk/src/commonMain/moko-resources/base/` |
|
||
| Shared Mihon / upstream behavior | `i18n/` | **`MR`** | `i18n/src/commonMain/moko-resources/base/` |
|
||
| TachiyomiSY-only | `i18n-sy/` | **`SYMR`** | `i18n-sy/src/commonMain/moko-resources/base/` |
|
||
|
||
**Hard rules:**
|
||
|
||
- **Never** add Komikku-specific strings to `i18n/` or `i18n-sy/`.
|
||
- **Never** edit non-`base` locale `strings.xml` or `plurals.xml` files in `i18n-kmk/`, `i18n/`, or `i18n-sy/` (Weblate owns translations).
|
||
- Import: `import tachiyomi.i18n.kmk.KMR` for Komikku strings.
|
||
- If a change is inside `// KMK -->` … `// KMK <--` or adds Komikku-only behavior, default to **`KMR` + `i18n-kmk`**.
|
||
|
||
**Self-check before finishing:** `git diff` must not add new `<string name="…">` or `<plurals name="…">` entries under non-`base` locales in `i18n-kmk/src/`, `i18n/src/`, or `i18n-sy/src/`.
|
||
|
||
### Formatting & build verification
|
||
|
||
**“Build passes” is not enough.** After Kotlin/XML edits, run **in this order** before marking work complete:
|
||
|
||
```bash
|
||
./gradlew spotlessApply # fix formatting
|
||
./gradlew spotlessCheck # must pass (same as CI)
|
||
./gradlew assembleDebug # or :app:compileDebugKotlin for a faster compile-only check
|
||
```
|
||
|
||
- **Do not** skip `spotlessCheck` when verifying changes.
|
||
- If `spotlessCheck` fails, run `spotlessApply` and re-run `spotlessCheck`.
|
||
- On Cloud VM, export `ANDROID_HOME` and `JAVA_HOME` first (see [Cursor Cloud](#cursor-cloud-specific-instructions)).
|
||
|
||
---
|
||
|
||
## Module layout
|
||
|
||
| Module | Purpose |
|
||
|--------|---------|
|
||
| `app/` | UI (`eu.kanade.*`, `exh/`, `mihon/`), DI, workers, build variants |
|
||
| `domain/` | Use cases in `…/interactor/` (e.g. `GetManga`), models, repo interfaces |
|
||
| `data/` | SQLDelight DB, `*RepositoryImpl` (`tachiyomi.data.*`) |
|
||
| `core:common/` | Network (OkHttp), security, storage, shared utils |
|
||
| `core:archive/` | CBZ/archive reading with optional encryption |
|
||
| `core-metadata/` | Comic-info metadata parsing |
|
||
| `source-api/` / `source-local/` | Extension `Source` API + local source |
|
||
| `presentation-core/` | Shared Compose components |
|
||
| `presentation-widget/` | Home-screen Glance widget |
|
||
| `i18n/` | Mihon strings → `MR` (moko-resources) |
|
||
| `i18n-kmk/` | Komikku strings → `KMR` |
|
||
| `i18n-sy/` | TachiyomiSY strings → `SYMR` |
|
||
| `flagkit/` | Country-flag drawables |
|
||
| `telemetry/` | Firebase/Crashlytics (noop unless `-Pinclude-telemetry`) |
|
||
| `macrobenchmark/` | Macrobenchmark tests |
|
||
|
||
Dependency flow: `app` → `domain` → `source-api`; `data` implements `domain` repos.
|
||
|
||
Version catalogs: `gradle/libs.versions.toml`, `kotlinx.versions.toml`, `androidx.versions.toml`, `compose.versions.toml`, `sy.versions.toml`.
|
||
|
||
---
|
||
|
||
## Architecture
|
||
|
||
**DI** – `uy.kohesive.injekt` (not Hilt). Register in `AppModule.kt`, `DomainModule.kt`, `KMKDomainModule.kt`, `SYDomainModule.kt` via `addSingleton` / `addSingletonFactory`. Resolve with `Injekt.get<T>()` or `injectLazy<T>()`.
|
||
|
||
**UI & navigation** – [Voyager](https://voyager.adriel.cafe/): `Screen` in `eu.kanade.tachiyomi.ui.*`, composables in `eu.kanade.presentation.*`. Base type: `eu.kanade.presentation.util.Screen`. State via `rememberScreenModel { … }`; most models extend `StateScreenModel<State>` or bases like `SearchScreenModel`; some use plain `ScreenModel`. Prefer `screenModelScope` and `ioCoroutineScope`; use `launchIO` / `withIOContext` from `tachiyomi.core.common.util.lang`. `rememberCoroutineScope()` is fine in Compose; long-lived services may use their own `CoroutineScope`.
|
||
|
||
**Activities (not Voyager)** – `MainActivity` (shell), `ReaderActivity` + `ReaderViewModel`, `WebViewActivity`, `UnlockActivity`, OAuth login activities, `DeepLinkActivity`. Reader: `ReaderActivity.newIntent(context, mangaId, chapterId)`. Web: both `WebViewScreen` (Voyager) and `WebViewActivity.newIntent(...)`.
|
||
|
||
Example: `DeepLinkScreen` + `DeepLinkScreenModel` in `app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/`.
|
||
|
||
**Domain / data** – One class per operation under `domain/…/interactor/` (verb names, not `*Interactor` suffix). Also `app/src/main/java/eu/kanade/domain/…/interactor/` for app-specific cases. Wire repos in `eu.kanade.domain.DomainModule.kt` (+ `KMKDomainModule`, `SYDomainModule`).
|
||
|
||
**Database** – SQLDelight in `data/src/main/sqldelight/tachiyomi/` (`.sq` queries, `migrations/*.sqm`). After schema changes add a new `.sqm` and often `// KMK` blocks in `.sq` / mappers. Regenerate: `./gradlew :data:generateSqlDelightInterface` (or any compile that touches `:data`).
|
||
|
||
**App preference migrations** – `app/src/main/java/mihon/core/migration/migrations/` (`mihon.core.migration.Migration`).
|
||
|
||
**Images** – Coil 3 (`coil3.*`, `context.imageLoader`). No Glide/Picasso.
|
||
|
||
---
|
||
|
||
## Komikku-specific work
|
||
|
||
- **Strings:** see [Mandatory rules – Internationalization](#mandatory-rules-for-ai-agents). Summary: Komikku → **`KMR`** / `i18n-kmk/…/base/` only.
|
||
- Do not edit locale `strings.xml` in `i18n/` or `i18n-sy/` except when syncing upstream; translations via [Weblate](https://hosted.weblate.org/engage/komikku-app/).
|
||
- Komikku code/DI: search `// KMK` (e.g. `KMKDomainModule`, `HideCategory`, library-update errors).
|
||
- Prefs: `eu.kanade.domain.*.service.*Preferences` (e.g. `SourcePreferences.relatedMangas()`).
|
||
|
||
**Examples (Komikku → `i18n-kmk`, not `i18n`):** library update error UI, sync-before-update messages, WebDAV/Discord settings, updater notifications, `mihon/feature/*` Komikku screens.
|
||
|
||
---
|
||
|
||
## Extensions & sources
|
||
|
||
- Catalog sources: installable APK extensions (not in this repo).
|
||
- In-repo: delegated sources and metadata in `exh/` (E-Hentai, NHentai, MangaDex, `exh/recs/`).
|
||
- `source-api`: `eu.kanade.tachiyomi.source.*` — avoid breaking extension ABI.
|
||
|
||
---
|
||
|
||
## Build & CI
|
||
|
||
Build types: `debug` (`.dev`), `release`, `releaseTest` (`.rt`), `foss` (`.foss`), `preview` (`.beta`, CI default), `benchmark`.
|
||
|
||
Gradle `-P` flags (`buildSrc/.../BuildConfig.kt`):
|
||
|
||
| Flag | Effect |
|
||
|------|--------|
|
||
| `include-telemetry` | Firebase Analytics + Crashlytics |
|
||
| `enable-updater` | In-app update checker |
|
||
| `disable-code-shrink` | Skip R8 minification |
|
||
| `include-dependency-info` | Dependency metadata in APK |
|
||
|
||
```bash
|
||
./gradlew spotlessApply # format (run before spotlessCheck)
|
||
./gradlew spotlessCheck # REQUIRED before considering work done (CI gate)
|
||
./gradlew assemblePreview # main CI/dev APK
|
||
./gradlew assemblePreview -Pinclude-telemetry -Penable-updater # full upstream CI build
|
||
./gradlew testReleaseUnitTest # CI unit tests (or ./gradlew test for all modules)
|
||
./gradlew installDebug # device install
|
||
./gradlew :data:generateSqlDelightInterface # after .sq / .sqm changes
|
||
```
|
||
|
||
**Agent verification checklist (minimum):** `spotlessApply` → `spotlessCheck` → `assembleDebug` (or `compileDebugKotlin` only if the user asked for a quick compile check—but still run Spotless).
|
||
|
||
JDK **17**.
|
||
|
||
---
|
||
|
||
## Fork-origin markers
|
||
|
||
Preserve inline blocks when editing:
|
||
|
||
```kotlin
|
||
// KMK --> … // KMK <-- Komikku
|
||
// SY --> … // SY <-- TachiyomiSY
|
||
// EXH --> … // EXH <-- E-Hentai / exh (existing); prefer KMK for new Komikku-only code
|
||
```
|
||
|
||
Package roots: `eu.kanade.tachiyomi.*` (legacy UI), `tachiyomi.*` (domain/data), `mihon.*` (Mihon upstream), `exh.*` (enhanced sources).
|
||
|
||
---
|
||
|
||
## Tests
|
||
|
||
- Unit tests: `domain/src/test/`; app: `app/src/test/.../MigratorTest.kt`. No broad UI test suite.
|
||
|
||
---
|
||
|
||
## Conventions
|
||
|
||
- **Logging** – Prefer `xLogE()` / `xLog()` helpers from `exh.log` for Komikku code, Mihon uses `logcat { }` from `tachiyomi.core.common.util.system`. Avoid raw `android.util.Log`.
|
||
- **Formatting** – Spotless + ktlint (`buildSrc/.../mihon.code.lint.gradle.kts`). Agents **must** run `spotlessApply` and `spotlessCheck` (see [Mandatory rules](#mandatory-rules-for-ai-agents)).
|
||
- **Fork edits** – New Komikku features inside `// KMK` islands; keep `// SY` / `// EXH` blocks intact when merging upstream.
|
||
|
||
---
|
||
|
||
## Key files
|
||
|
||
- `App.kt` – Injekt bootstrap, logging setup
|
||
- `MainActivity.kt` – Voyager host
|
||
- `app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt` – core DI
|
||
- `app/src/main/java/eu/kanade/domain/DomainModule.kt` – domain interactors
|
||
- `buildSrc/.../BuildConfig.kt`, `AndroidConfig.kt` – flags, SDK versions
|
||
- `app/build.gradle.kts`, `settings.gradle.kts`
|
||
|
||
---
|
||
|
||
## Cursor Cloud specific instructions
|
||
|
||
### Environment
|
||
|
||
The VM update script installs the Android SDK (platform 36, build-tools 35.0.1, platform-tools, cmdline-tools) into `/opt/android-sdk` and writes `local.properties` with `sdk.dir`. JDK 21 is pre-installed and works fine for compiling to JVM target 17. `ANDROID_HOME`, `JAVA_HOME`, and `PATH` are set in `~/.bashrc`.
|
||
|
||
### Running key commands
|
||
|
||
All Gradle commands require the environment variables set above. Export them before invoking `./gradlew` if running in a fresh shell:
|
||
|
||
```bash
|
||
export ANDROID_HOME=/opt/android-sdk
|
||
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
|
||
```
|
||
|
||
| Task | Command |
|
||
|------|---------|
|
||
| **Required format fix** | `./gradlew spotlessApply` (run first after code edits) |
|
||
| **Required format gate** | `./gradlew spotlessCheck` (must pass before task is done) |
|
||
| Debug APK build | `./gradlew assembleDebug` |
|
||
| Preview APK build (CI) | `./gradlew assemblePreview` |
|
||
| Unit tests (CI) | `./gradlew testReleaseUnitTest` |
|
||
| All module tests | `./gradlew test` |
|
||
| SQLDelight codegen | `./gradlew :data:generateSqlDelightInterface` |
|
||
|
||
### Gotchas
|
||
|
||
- First Gradle build downloads ~1 GB of dependencies; subsequent builds use the Gradle cache and are much faster.
|
||
- `local.properties` is `.gitignore`d — it must be recreated if missing (the update script handles this).
|
||
- No Android emulator or device is available on the Cloud VM, so `installDebug` will fail. Build verification is done via `assembleDebug`.
|
||
- `google-services.json` and `client_secrets.json` are not present (CI secrets); builds without `-Pinclude-telemetry` succeed without them.
|
||
- Gradle daemon may use significant memory (`-Xmx4g` in `gradle.properties`). If OOM occurs, kill and restart the daemon with `./gradlew --stop`.
|