CLAUDE.md
CLAUDE.mdCLAUDE.mdroot
Quality
78/100
Scores the file, not the repository.Length
1,396 words
15 headings · 3 code blocksRepository
17k
— · pushed 5 days agoLast changed
3 days ago
First indexed 3 days ago.1# Komi Store23Cross-platform app store for GitHub + Codeberg + Forgejo releases. **Kotlin Multiplatform** + **Compose Multiplatform**. Android (min API 26) + Desktop (JVM: Win/macOS/Linux). Package `zed.rainxch.githubstore`. Version 1.8.3 (code 18). Target SDK 36.45## Build67```bash8./gradlew :composeApp:assembleDebug # Android9./gradlew :composeApp:run # Desktop dev10./gradlew :composeApp:packageExe :composeApp:packageMsi # Win installer11./gradlew :composeApp:packageDmg :composeApp:packagePkg # macOS12./gradlew :composeApp:packageDeb :composeApp:packageRpm # Linux13./gradlew build # full14```1516JDK 21+. Android SDK for Android.1718## Structure1920```text21composeApp/ # entry points, navigation, DI wiring (commonMain / androidMain / jvmMain)22core/23 domain/ # interfaces, models, use cases (no framework deps)24 data/ # repos, Ktor, Room, Koin, platform impls25 presentation/ # Material 3 theme + reusable components + 14-locale strings26feature/27 apps auth details dev-profile favourites homeP profile recently-viewed search starred tweaks28build-logic/convention/ # convention plugins29```3031Each feature: up to 3 sub-modules (`domain/`, `data/`, `presentation/`). `favourites`, `starred`, `recently-viewed` are presentation-only.3233## Architecture3435Clean Architecture + MVVM. Layers: **Domain** (contracts), **Data** (Ktor + Room + Koin DI), **Presentation** (ViewModels with `StateFlow`/`Channel`, Compose).3637### State pattern (every screen)3839```kotlin40class XViewModel : ViewModel() {41 private val _state = MutableStateFlow(XState())42 val state = _state.asStateFlow() // or .stateIn(WhileSubscribed)43 private val _events = Channel<XEvent>()44 val events = _events.receiveAsFlow()45 fun onAction(action: XAction) { ... }46}47```4849`State` = data class. `Action` = sealed (user input). `Event` = sealed (one-off effects).5051### Navigation5253`@Serializable` sealed interface `GithubStoreGraph` in `composeApp/.../app/navigation/`. Routes: `HomeScreen`, `SearchScreen`, `AuthenticationScreen`, `ProfileScreen`, `TweaksScreen`, `FavouritesScreen`, `StarredReposScreen`, `RecentlyViewedScreen`, `AppsScreen`, `OnboardingScreen`, `ExternalImportScreen`, `MirrorPickerScreen`, `StarredPickerScreen`, `SkippedUpdatesScreen`, `HiddenRepositoriesScreen`, `WhatsNewHistoryScreen`, `AnnouncementsScreen`, `HostTokensScreen`, `DetailsScreen(repositoryId, owner, repo, isComingFromUpdate, sourceHost)`, `DeveloperProfileScreen(username)`. `DetailsScreen.sourceHost` is non-null for Codeberg / Forgejo / custom-forge repos — routes all `DetailsRepository` calls through `ForgejoClientRegistry` instead of the GitHub-backed default path.5455### DI5657Koin. Feature modules in `data/di/SharedModule.kt`. ViewModels in `composeApp/.../app/di/ViewModelsModule.kt` (`viewModelOf(::X)` or explicit `viewModel { ... }`). Wired in `initKoin.kt`.5859## Core repositories (`core/domain`)6061`FavouritesRepository`, `StarredRepository`, `InstalledAppsRepository`, `SeenReposRepository`, `HiddenReposRepository`, `SearchHistoryRepository`, `TweaksRepository`, `AuthenticationState`, `ThemesRepository`, `ProxyRepository`, `RateLimitRepository`, `ExternalImportRepository`, `TelemetryRepository`, `HostTokenRepository` (per-host PATs, KSafe-encrypted). Network: `ForgejoApiClient` + `ForgejoClientRegistry` (per-host Ktor clients, thread-safe via Mutex, proxy-aware, closes cached engines on shutdown / proxy change). Util: `AssetVariant` (token/glob/stem fingerprinting), `assetPlatformOf`, `RepoIdCodec` (23-bit host fingerprint + 40-bit raw id packed into the existing 64-bit `repoId` slot — sign bit = foreign source), `RepositoryUrlParser` (recognises GitHub + Codeberg + gitea.com + git.disroot.org + user-added forge hosts). System interfaces: `Installer`, `InstallerStatusProvider`, `PackageMonitor`, `SystemInstallSerializer`.6263## Tech6465Kotlin 2.3.10, Compose Multiplatform 1.10.3, Ktor 3.4.0, Room 2.8.4, Koin 4.1.1, kotlinx.serialization 1.10.0, DataStore 1.2.0, Landscapist 2.9.5, Kermit 2.0.8, MOKO Permissions 0.20.1, Navigation Compose 2.9.2, multiplatform-markdown-renderer 0.39.2, Shizuku 13.1.5, WorkManager 2.11.1, kotlinx.datetime 0.7.1. Versions in `gradle/libs.versions.toml`.6667## Convention plugins (`build-logic/convention/`)6869`convention.kmp.library` (domain/data), `convention.cmp.library` (core/presentation), `convention.cmp.feature` (feature presentation), `convention.cmp.application` (main app), `convention.room`, `convention.buildkonfig`.7071## Adding a feature72731. `feature/<name>/{domain,data,presentation}/` with appropriate convention plugin742. `include` in `settings.gradle.kts`753. Domain interfaces → impl + Koin module in `data/di/SharedModule.kt` → ViewModel + Screen764. Route in `GithubStoreGraph.kt` + wire in `AppNavigation.kt` + register Koin in `initKoin.kt`7778## Key configuration7980- **GitHub OAuth:** `GITHUB_CLIENT_ID` in `local.properties`. Deep links: `githubstore://auth` (web-OAuth handoff), `githubstore://callback` (legacy device-flow leftover), `githubstore://repo`, `githubstore://apps`.81- **Shizuku (Android):** silent install via `ShizukuProvider` → AIDL → `pm install -S`. Fallback to standard installer on failure.82- **Desktop logs:** `CrashReporter` (first line of `DesktopApp.main`) tees stdout/stderr to rotating `session.log` + writes `crash-<ts>.log` on uncaught. Paths: `~/Library/Logs/GitHub-Store/` (macOS), `%LOCALAPPDATA%/GitHub-Store/logs/` (Win), `$XDG_STATE_HOME/GitHub-Store/logs/` (Linux). Android = Logcat.83- **macOS distribution:** Homebrew cask in tap `openhub-store/tap` (separate repo `homebrew-tap`). `brew install --cask github-store`. Unsigned at present — user must `xattr -dr com.apple.quarantine /Applications/GitHub-Store.app` after install. CI builds `.dmg` + `.pkg` on every push to `generate-installers`; tap cask updates automatically on release.84- **`X-GitHub-Token` header:** Client attaches when `TokenStore.currentToken()` is non-null on `/v1/search`, `/v1/search/explore`, `/v1/repo`, `/v1/releases`, `/v1/readme`, `/v1/user`. Backend re-sends as `Authorization: token $token` to GitHub. Without it, backend round-robins a 4-token service pool. Upstream 401 remapped to backend `502` (handled like "GitHub unreachable" — fall back via `shouldFallbackToGithubOrRethrow`). `429` = no fallback (same wall), only backoff. `UnauthorizedInterceptor` only on direct-GitHub client; `AuthenticationStateImpl` debounces consecutive 401s by token snapshot.85- **Auth flow (web-OAuth-first):** Primary path is web OAuth with PKCE + handoff. `feature/auth/data/crypto/PkceGenerator` mints `(state, codeVerifier, codeChallenge)`; `WebAuthApi.register` POSTs verifier + challenge + state to `https://github-store.org/auth/register` (Cloudflare Worker stashes them in Workers KV) and returns `authUrl`. User opens it, authorizes on `github.com`, GitHub redirects to `github-store.org/auth/callback?code&state` where the Worker exchanges the code via `api.github-store.org` (backend stores `(handoffId → access_token)` for 60s in Postgres with atomic `DELETE…RETURNING`), then bounces back to `githubstore://auth?h=<handoffId>`. App reads handoff via `WebAuthApi.consumeHandoff` (GETDEL semantics). Secondary path: device flow via backend `/v1/auth/device/start` + `/poll`, `AuthPath` (`Backend`|`Direct`) tracked in `SavedStateHandle`, only escalates `Backend → Direct` on infra errors. Tertiary: paste a Personal Access Token (`signInWithPat` — validates against `/user`, persists optimistically when GitHub unreachable). Backend rate limits: 10 device-starts/hr, 200 device-polls/hr per IP. Endpoints in `core/data/network/BackendEndpoints.kt` (`BACKEND_ORIGIN`, `WEB_ORIGIN`).86- **Windows installer signing (SignPath Foundation):** CI workflow `.github/workflows/build-desktop-platforms.yml` job `sign-windows` after every push to `generate-installers` branch. Action pinned to commit SHA (not `@v2`). Secrets: `SIGNPATH_API_TOKEN`, `SIGNPATH_ORGANIZATION_ID` (`1ecf111e-...`). Variable `SIGNPATH_SIGNING_POLICY_SLUG` = `test-signing` until prod cert issued; flip to `release-signing`. Project slug `GitHub-Store`, artifact config slug `initial`. Unsigned artifact deleted post-sign; only `windows-installers-signed` reaches the draft release.87- **WinGet publish:** `.github/workflows/winget-publish.yml` fires on `release: [released]`. Action `vedantmgoyal9/winget-releaser@main`. Secret `WINGET_TOKEN` = PAT with `Contents+Pull requests: write` on `OpenHub-Store/winget-pkgs` (fork of `microsoft/winget-pkgs`). Pin `fork-user: OpenHub-Store` explicitly so the action doesn't infer from token owner.88- **Forges (Codeberg / Forgejo / Gitea):** `ForgejoApiClient` per host (60s req / 30s connect+socket timeouts, exponential retry on 5xx + IOException). `ForgejoClientRegistry.clientFor(host)` cached + Mutex-guarded. Direct-to-forge — no backend mediator. `RepoIdCodec` packs host fingerprint into `repoId` so the existing GitHub-shaped schema survives. README via `/contents/README.md?ref={branch}` (Forgejo has NO `/readme` endpoint). License sniffed from `/contents/LICENSE` regex against SPDX headers. Downloads aggregated by summing `asset.download_count` across releases.89- **Per-host PATs:** `HostTokenRepository` stores `{host, token, label, createdAt}` rows AES-256-GCM encrypted via KSafe. `HostTokenInterceptor` (Ktor plugin) injects `Authorization: token $pat` on matched host. `HostNames.apiHostToTokenHost` maps `api.github.com → github.com` so the GitHub-direct client looks up the right PAT. UI at `Tweaks → Access Tokens` (`HostTokensScreen`).90- **KSafe:** AES-256-GCM with hardware-backed Keystore on Android. Wraps every persisted credential / pref via `core/data/secure/KSafeSafe.kt` extension funcs (`safeGet`, `safePut`, `safeDelete`, `safeGetFlow`) — surface log + return null/false on transient failure instead of throwing through coroutine scopes.91- **Translation providers:** `TranslationProvider` enum = `GOOGLE`, `YOUDAO`, `LIBRE_TRANSLATE`, `DEEPL`, `MICROSOFT`. Each per-provider config persisted via `TweaksRepository` (KSafe-encrypted). `TranslationRepositoryImpl.resolveTranslator()` picks the impl. LibreTranslate defaults to the bundled `translate.disroot.org` mirror when user URL pref blank. DeepL auto-routes `:fx`-suffixed keys to `api-free.deepl.com`. Microsoft uses No-Trace by default — text never stored, never used for training.92- **Gradle:** Config + build cache enabled. 4GB Gradle heap, 3GB Kotlin daemon. Official Kotlin style.9394## Active skills (apply on matching domain)9596- **caveman** — session default, terse output.97- **karpathy-guidelines** — anti-overcomplication, minimal diffs, surface assumptions, verifiable success criteria. Every coding task.98- **one-skill-to-rule-them-all** — watch for skill-capture opportunities during multi-step work.99- **gsd-inbox** - Triage open GitHub issues + PRs against templates. Our exact pattern — automate the "check issue #N, draft reply, ship fix" loop.100- **gsd-ship** - Create PR + review + prep for merge. Every task ends here.101- **gsd-quick** - Trivial task with atomic commits + state tracking. Matches our small-commit policy.102- **gsd-debug** - Systematic debugging with persistent state across context resets. For bug-hunt cycles.103- **android-* skills** (`~/.claude/skills/android/`) — auto-fire by description match; apply when in matching domain:104 - `android-compose-ui` — composables, recomposition, animations, modifiers, design system105 - `android-data-layer` — repos, DTOs, Room, Ktor, mappers106 - `android-di-koin` — Koin module setup, ViewModel injection107 - `android-error-handling` — Result wrapper, typed errors108 - `android-module-structure` — feature-layered modules, convention plugins109 - `android-navigation` — type-safe Compose nav110 - `android-presentation-mvi` — State/Action/Event, Root/Screen split, UiText, SavedStateHandle111 - `android-testing` — testing patterns112113## Conventions114115- Packages `zed.rainxch.{module}.{layer}`116- Private state fields prefix `_state`117- Sealed routes/actions/events118- Repository pattern: interface in `domain/`, impl in `data/`119- Source sets: `commonMain` shared, `androidMain`, `jvmMain`120- **No KDoc, no inline comments** unless the user explicitly asks. No function/class docs. Inline only for non-obvious invariants, tricky concurrency, workarounds. Applies globally.121- Feature-specific guidance in each `feature/*/CLAUDE.md`122123## Approach124125- Read existing files before writing. Don't re-read unless changed.126- Thorough in reasoning, concise in output.127- Skip files over 100KB unless required.128- No sycophantic openers or closing fluff.129- No emojis or em-dashes.130- Do not guess APIs, versions, flags, commit SHAs, or package names. Verify by reading code or docs before asserting, researching if necessary.131
Also in kurikomi-labs/komi-store
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| kurikomi-labs/komi-storefeature/dev-profile/CLAUDE.md · 17k | CLAUDE.md | arch | 40/100 | 3 days ago | |
| kurikomi-labs/komi-storefeature/favourites/CLAUDE.md · 17k | CLAUDE.md | arch | 40/100 | 3 days ago | |
| kurikomi-labs/komi-storefeature/home/CLAUDE.md · 17k | CLAUDE.md | arch | 54/100 | 3 days ago | |
| kurikomi-labs/komi-storefeature/profile/CLAUDE.md · 17k | CLAUDE.md | arch | 54/100 | 3 days ago | |
| kurikomi-labs/komi-storefeature/tweaks/CLAUDE.md · 17k | CLAUDE.md | archmonorepo | 54/100 | 3 days ago | |
| kurikomi-labs/komi-storefeature/recently-viewed/CLAUDE.md · 17k | CLAUDE.md | arch | 35/100 | 3 days ago | |
| kurikomi-labs/komi-storefeature/search/CLAUDE.md · 17k | CLAUDE.md | arch | 54/100 | 3 days ago | |
| kurikomi-labs/komi-storefeature/starred/CLAUDE.md · 17k | CLAUDE.md | arch | 54/100 | 3 days ago | |
| kurikomi-labs/komi-storeAGENTS.md · 17k | AGENTS.md | buildlint-formatstylearch+1 | 97/100 | 3 days ago | |
| kurikomi-labs/komi-storefeature/apps/CLAUDE.md · 17k | CLAUDE.md | arch | 58/100 | 3 days ago | |
| kurikomi-labs/komi-storefeature/auth/CLAUDE.md · 17k | CLAUDE.md | archsecurity | 58/100 | 3 days ago | |
| kurikomi-labs/komi-storefeature/details/CLAUDE.md · 17k | CLAUDE.md | arch | 53/100 | 3 days ago |
Diff against feature/dev-profile/CLAUDE.md Diff against feature/favourites/CLAUDE.md Diff against feature/home/CLAUDE.md Diff against feature/profile/CLAUDE.md Diff against feature/tweaks/CLAUDE.md Diff against feature/recently-viewed/CLAUDE.md Diff against feature/search/CLAUDE.md Diff against feature/starred/CLAUDE.md Diff against AGENTS.md Diff against feature/apps/CLAUDE.md Diff against feature/auth/CLAUDE.md Diff against feature/details/CLAUDE.md
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| Adit-Jain-srm/NightmareNetCLAUDE.md · 45 | CLAUDE.md | buildtestlint-formatstyle+6 | 100/100 | 3 days ago | |
| nimbalyst/nimbalystpackages/android/CLAUDE.md · 1.4k | CLAUDE.md | setupbuildstylearch+2 | 100/100 | 3 days ago | |
| stacklok/toolhiveCLAUDE.md · 2.0k | CLAUDE.md | buildteststylearch+4 | 100/100 | 3 days ago | |
| dotCMS/corecore-web/CLAUDE.md · 949 | CLAUDE.md | teststylearchtesting-strategy+3 | 100/100 | 3 days ago | |
| microsoft/playwrightCLAUDE.md · 94k | CLAUDE.md | buildtestlint-formatstyle+7 | 100/100 | 3 days ago | |
| filamentphp/filamentCLAUDE.md · 32k | CLAUDE.md | buildtestlint-formatstyle+7 | 100/100 | 3 days ago | |
| livewire/livewireCLAUDE.md · 24k | CLAUDE.md | setupbuildteststyle+4 | 100/100 | 3 days ago | |
| bagisto/bagistoCLAUDE.md · 28k | CLAUDE.md | setupbuildteststyle+5 | 100/100 | 3 days ago |
