account-state
Account state and in-memory event store patterns in Amethyst. Use when working with `Account.kt` (per-user state objects — `kind3FollowList`, `nip65RelayList`,…
Platform abstraction decision-making for Amethyst KMP project. Guides when to abstract vs keep platform-specific, source set placement (commonMain, jvmAndroid, platform-specific), expect/actual patterns. Covers primary targets (Android, JVM/Desktop, iOS — all mature) with
$ npx -y skills add vitorpamplona/amethyst --skill kotlin-multiplatform --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/kotlin-multiplatformContext preview
The summary Claude sees to decide when to auto-load this skill.
Platform abstraction decision-making for Amethyst KMP project. Guides when to abstract vs keep platform-specific, source set placement (commonMain, jvmAndroid, platform-specific), expect/actual patterns. Covers primary targets (Android, JVM/Desktop, iOS — all mature) with
name: kotlin-multiplatform
description: |
Platform abstraction decision-making for Amethyst KMP project. Guides when to abstract vs keep platform-specific,
source set placement (commonMain, jvmAndroid, platform-specific), expect/actual patterns. Covers primary targets
(Android, JVM/Desktop, iOS — all mature) with web/wasm as possible future targets. Integrates with gradle-expert for dependency issues.
Triggers on: abstraction decisions ("should I share this?"), source set placement questions, expect/actual creation,
build.gradle.kts work, incorrect placement detection, KMP dependency suggestions.Expert guidance for KMP architecture in Amethyst - deciding what to share vs keep platform-specific.
Making platform abstraction decisions:
**Central question:** "Should this code be reused across platforms?"
Follow this decision path (< 1 minute):
Q: Is it used by 2+ platforms? ├─ NO → Keep platform-specific │ Example: Android-only permission handling │ └─ YES → Continue ↓ Q: Is it pure Kotlin (no platform APIs)? ├─ YES → commonMain │ Example: Nostr event parsing, business rules │ └─ NO → Continue ↓ Q: Does it vary by platform or by JVM vs non-JVM? ├─ By platform (Android ≠ iOS ≠ Desktop) │ → expect/actual │ Example: Secp256k1Instance (uses different security APIs) │ ├─ By JVM (Android = Desktop ≠ iOS/web) │ → jvmAndroid │ Example: Jackson JSON parsing (JVM library) │ └─ Complex/UI-related → Keep platform-specific Example: Navigation (Activity vs Window too different) Final check: Q: Maintenance cost of abstraction < duplication cost? ├─ YES → Proceed with abstraction └─ NO → Duplicate (simpler)
**Crypto → expect/actual:**
// commonMain - expect declaration
expect object Secp256k1Instance {
fun signSchnorr(data: ByteArray, privKey: ByteArray): ByteArray
}
// androidMain - uses Android Keystore
// jvmMain - uses Desktop JVM crypto
// iosMain - uses iOS Security framework**Why:** Each platform has different security APIs.
**JSON parsing → jvmAndroid:**
// quartz/build.gradle.kts
val jvmAndroid = create("jvmAndroid") {
api(libs.jackson.module.kotlin)
}**Why:** Jackson is JVM-only, works on Android + Desktop, not iOS/web.
**Navigation → platform-specific:**
**Why:** UI paradigms fundamentally different.
Think of source sets as a dependency graph, not folders.
┌─────────────────────────────────────────────┐
│ commonMain = Contract (pure Kotlin) │
│ - Business logic, protocol, data models │
│ - No platform APIs │
└────────────┬────────────────────────────────┘
│
├──────────────────────┬────────────────────
│ │
▼ ▼
┌───────────────────┐ ┌──────────────────┐
│ jvmAndroid │ │ iosMain │
│ JVM libs shared │ │ iOS common │
│ - Jackson │ │ │
│ - OkHttp │ └────┬─────────────┘
└───┬───────────┬───┘ │
│ │ │
▼ ▼ ├─→ iosArm64Main
┌─────────┐ ┌──────────┐ └─→ iosSimulatorArm64Main
│android │ │jvmMain │
│Main │ │(Desktop) │
└─────────┘ └──────────┘
Future: jsMain, wasmMain**Key insight:** jvmAndroid is NOT a platform - it's a shared JVM layer.
**Unique to Amethyst.** Shares JVM libraries between Android + Desktop.
Use jvmAndroid when:
Do NOT use jvmAndroid for:
// Must be defined BEFORE androidMain and jvmMain
val jvmAndroid = create("jvmAndroid") {
dependsOn(commonMain.get())
dependencies {
api(libs.jackson.module.kotlin) // JSON parsing - JVM only
api(libs.url.detector) // URL extraction - JVM only
implementation(libs.okhttp) // HTTP client - JVM only
}
}
// Both depend on jvmAndroid
jvmMain { dependsOn(jvmAndroid) }
androidMain { dependsOn(jvmAndroid) }**Why Jackson in jvmAndroid, not commonMain?**
**Web/wasm consideration:** For future web support, consider migrating from Jackson → kotlinx.serialization (see Target-Specific Guidance).
Quick decision guidelines based on codebase patterns:
Account state and in-memory event store patterns in Amethyst. Use when working with `Account.kt` (per-user state objects — `kind3FollowList`, `nip65RelayList`,…
Patterns for extending `amy`, the Amethyst CLI in `cli/`. Use when adding an `amy <verb>` command, touching files under `cli/src/main/kotlin/…/cli/`, wiring a…
Android platform patterns for the `amethyst/` module. Use when working with (1) Android navigation (Navigation Compose, type-safe routes, bottom nav), (2)…
Signer abstraction patterns in Amethyst. Use when working with event signing, choosing between a local keypair (`NostrSignerInternal`), a remote NIP-46 bunker…
Advanced Compose Multiplatform UI patterns for shared composables. Use when working with visual UI components, state management patterns (remember,…
Use when writing or reviewing Jetpack Compose layout APIs, modifier parameters, modifier chain construction, hardcoded root layout decisions, or layout…