account-state
Account, LocalCache, User/Note and feeds in commons/model + commons/feeds: per-user XState objects (.flow),…
KMP placement and Gradle in Amethyst: which module or source set, expect/actual vs ports, verifyKmpPurity, extracting Android composables to commonsUI, build.gradle.kts/libs.versions.toml edits, secp256k1 JNI variants, R8.
Claude Code
$ npx -y skills add vitorpamplona/amethyst --skill kotlin-multiplatform --agent claude-code
Codex
$ npx skills add vitorpamplona/amethyst --skill kotlin-multiplatform -a codex -y
Cursor
$ npx skills add vitorpamplona/amethyst --skill kotlin-multiplatform -a cursor -y
Gemini CLI
$ npx skills add vitorpamplona/amethyst --skill kotlin-multiplatform -a gemini-cli -y
GitHub Copilot
$ npx skills add vitorpamplona/amethyst --skill kotlin-multiplatform -a github-copilot -y
OpenCode
$ npx skills add vitorpamplona/amethyst --skill kotlin-multiplatform -a opencode -y
Windsurf
$ npx skills add vitorpamplona/amethyst --skill kotlin-multiplatform -a windsurf -y
name: kotlin-multiplatform description: KMP placement and Gradle in Amethyst: which module or source set, expect/actual vs ports, verifyKmpPurity, extracting Android composables to commonsUI, build.gradle.kts/libs.versions.toml edits, secp256k1 JNI variants, R8.
Module roles, the "share vs keep native" rule and the CLI-safe boundary are in `AGENTS.md` (Architecture, Feature Workflow). Package placement inside `commons` / `commonsUI` is in `commons/ARCHITECTURE.md` ("Where does my code go?", "Source sets") and `commonsUI/ARCHITECTURE.md` ("Platform slots"). Read those first; this skill adds the source-set mechanics and the build traps.
`settings.gradle.kts` includes 13: `amethyst`, `nappletHost`, `benchmark`, `baselineprofile`, `geode`, `commons`, `commonsUI`, `nestsClient`, `marmotQuic`, `desktopApp`, `cli`, `relayBench`, `marmotBench`. Quartz is the external `libs.quartz` (vitorpamplona/quartz) and QUIC the external `libs.quic` (vitorpamplona/quic-kmp); `-PquartzDir=../quartz` / `-PquicKmpDir=../quic-kmp` build a local checkout instead (an included build, so quic's own quartz dependency follows).
| Source set | Holds | Targets | |---|---|---| | `commonMain` | Pure Kotlin. Compiled for iOS/native too, so no JVM APIs | all | | `jvmAndroid` | JVM libraries shared by Android + Desktop: OkHttp, `nestsClient`, NWC/LNURL | android, jvm | | `androidMain` / `jvmMain` | Platform actuals (Keystore/DataStore vs keyring, Coil bridges) | one each | | `iosMain`, `appleMain`, `nativeMain`, `linuxMain`, `macosMain` | quartz only (plus `iosMain` in commons/commonsUI) | Apple / native | | `skikoMain` | commonsUI code shared by Desktop and iOS | jvm, ios |
Targets: `quartz` builds android, jvm, iosArm64, iosSimulatorArm64, linuxX64, macosArm64. `commons` and `commonsUI` build android, jvm, iosArm64, iosSimulatorArm64. `nestsClient`, `marmotQuic` are android + jvm. There is no `iosX64`.
`jvmAndroid` is a custom intermediate set created with `create("jvmAndroid")` before `androidMain`/`jvmMain` depend on it (see `commons/build.gradle.kts`). Prefer the most common set that compiles: `commonMain` → `jvmAndroid` → platform.
kotlinx.serialization is the house JSON stack and OkHttp the house HTTP stack (`AGENTS.md` › Tech Stack). Nostr JSON (events, messages, filters, tags) goes through quartz's Kite (`OptimizedJsonMapper`, `KiteWriter`, `KitePretty`); quartz dropped Jackson in 2.4.0, so don't add Jackson back. Don't migrate JVM HTTP to Ktor. Jackson and OkHttp are banned in iOS-compiled sets, which the purity gate enforces.
`KmpLock`, `ConcurrentSet`, `SecureKeyStorage`, `isDebug`, `formattedDateTime` (commons), `rememberTextSharer`, `rememberShortNotice` (commonsUI). Put the actual in `jvmAndroid` when Android and Desktop share it.
state or services: `AccountViewModelHost`, `UserNoticeSink`, `LocalCacheHost`.
can draw: `LocalNotePlatform`, `LocalRichTextPlatform`.
`LibSodiumInstance` are plain `object`s in `quartz/.../utils/`.
behind a port or slot.
`gradle/kmp-purity.gradle.kts`, applied by `commons` and `commonsUI` (quartz keeps a copy) and wired into `check`, scans every iOS/native-compiled source set (`commonMain`, `iosMain`, `skikoMain`, …, and their tests) and fails on `com.fasterxml.jackson`, `okhttp3`, `System.currentTimeMillis` (use `TimeUtils.now()`), `Thread.sleep`, `java.util.UUID` (use `kotlin.uuid.Uuid`), `kotlin.jvm.Synchronized` or bare `synchronized(` (use `KmpLock`), `kotlin.jvm.Volatile` (use `kotlin.concurrent.Volatile`). CI runs it on Linux in the lint job; the macOS `test-commons-ios` / `test-commonsui-ios` jobs are authoritative. Run `./gradlew :commons:verifyKmpPurity :commonsUI:verifyKmpPurity` after touching `commonMain`. Quartz has its own copy of the task and its own iOS CI.
reference `libs.*`; never hardcode a version or copy one into docs. Check the license first (`AGENTS.md` › Dependency Licensing).
(`:commonsUI:testAndroidHostTest`, the native ones) run only when named; see `AGENTS.md` › Build Commands.
(`KotlinDependencyHandler`) has no `testFixtures(...)`. Use a top-level block with the configuration name as a string: `dependencies { "jvmAndroidTestImplementation"(testFixtures(project(":geode"))) }`.
`secp256k1-kmp-jni-android` in `androidMain`, `secp256k1-kmp-jni-jvm` in `jvmMain` and JVM tests. All three share one `version.ref`. A JVM classpath with the android variant fails at load with `UnsatisfiedLinkError`.
`-keep class com.vitorpamplona.quartz.** { *; }`. R8 can't see reflection, so a moved/renamed reflective class is a silent release break: before tagging, run `python3 tools/r8-verify/verify_reflection_contract.py amethyst/build/outputs/mapping/<variant>/` (the release workflow checks `playRelease` and `fdroidRelease`) and update `tools/r8-verify/reflection-contract.txt` when you move a listed class.
Steps and the Android → multiplatform replacement table: [references/extract-composable.md](references/extract-composable.md).
Account, LocalCache, User/Note and feeds in commons/model + commons/feeds: per-user XState objects (.flow),…
amy CLI in cli/: adding an `amy <verb>` command, Main.kt dispatch, Output.emit/--json contract, exit codes 0/1/2/124, ~/.amy…
Android shim in `amethyst/`: MainActivity, AndroidManifest/intent filters, :napplet process, play/fdroid flavors, R8 +…
NostrSigner in Amethyst: NostrSignerInternal (local key), NostrSignerRemote (NIP-46 bunker), NostrSignerExternal (NIP-55 Android…
Shared Compose UI in `commonsUI/`: screens, components, the nav shell (`ScreenLayoutSpec`, `AppShellLayout`, Navigation 3 back…
Desktop JVM shim in desktopApp/: AmethystDesktop window, DesktopMenuBar, platform ports, OS keyring; :desktopApp:run, packaging.…
Decompile Android APK, XAPK, JAR, and AAR files using jadx or Fernflower/Vineflower. Reverse engineer Android apps, extract HTTP…