Skip to content
Development
Skill

/gradle-expert

Build optimization, dependency resolution, and multi-module KMP troubleshooting for AmethystMultiplatform. Use when working with: (1) Gradle build files (build.gradle.kts, settings.gradle), (2) Version catalog (libs.versions.toml), (3) Build errors and dependency conflicts, (4)

From plugin
amethyst
1.6k30 skills3 commands
Install
$ npx -y skills add vitorpamplona/amethyst --skill gradle-expert --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/gradle-expert

Context preview

The summary Claude sees to decide when to auto-load this skill.

Build optimization, dependency resolution, and multi-module KMP troubleshooting for AmethystMultiplatform. Use when working with: (1) Gradle build files (build.gradle.kts, settings.gradle), (2) Version catalog (libs.versions.toml), (3) Build errors and dependency conflicts, (4)

SKILL.md

gradle-expert.SKILL.md
name: gradle-expert
description: Build optimization, dependency resolution, and multi-module KMP troubleshooting for AmethystMultiplatform. Use when working with: (1) Gradle build files (build.gradle.kts, settings.gradle), (2) Version catalog (libs.versions.toml), (3) Build errors and dependency conflicts, (4) Module dependencies and source sets, (5) Desktop packaging (DMG/MSI/DEB), (6) Build performance optimization, (7) Proguard/R8 configuration, (8) Common KMP + Android Gradle issues (Compose conflicts, secp256k1 JNI variants, source set problems).

Gradle Expert

Build system expertise for AmethystMultiplatform's 10-module KMP architecture (`amethyst`, `benchmark`, `quartz`, `geode`, `commons`, `quic`, `nestsClient`, `desktopApp`, `cli`, `quic-interop` — see `settings.gradle.kts`). Focus: practical troubleshooting, dependency resolution, and project-specific optimizations.

Build Architecture Mental Model

The core app stack is **4 layers** (the other modules hang off it: `cli` and `geode` are JVM apps over `commons`/`quartz`, `nestsClient` sits on `quic`, `benchmark` and `quic-interop` are test harnesses):

┌─────────────┬─────────────┐
│ :amethyst   │ :desktopApp │  ← Platform apps (navigation, layouts)
│ (Android)   │    (JVM)    │
└──────┬──────┴──────┬──────┘
       │             │
       └──────┬──────┘
              ▼
      ┌─────────────┐
      │  :commons   │           ← Shared UI (KMP with jvmAndroid)
      │  (KMP UI)   │
      └──────┬──────┘
             ▼
      ┌─────────────┐
      │  :quartz    │           ← Core library (KMP: Android/JVM/iOS)
      │(KMP Library)│
      └─────────────┘

**Key insight:** Dependencies flow DOWN. Lower modules never depend on upper modules. This enables code sharing without circular dependencies.

**The jvmAndroid pattern:** Unique to this project. A custom source set between commonMain and {androidMain, jvmMain} for JVM-specific code shared by Android and Desktop. Not standard KMP, but critical for this architecture.

Version Catalog Philosophy

All dependencies centralized in `gradle/libs.versions.toml`. Think "single source of truth."

**Pattern:**

[versions]
kotlin = "2.3.0"

[libraries]
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }

[plugins]
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }

**Usage:**

dependencies {
    implementation(libs.okhttp)  // Type-safe, IDE-autocompleted
}

**Critical alignments:**

  • **Kotlin ecosystem:** All Kotlin plugins MUST share same version
  • **Compose ecosystem:** Compose Multiplatform version → Kotlin version (check compatibility matrix)
  • **secp256k1 variants:** All three variants (common, jni-android, jni-jvm) MUST share same version

See [references/version-catalog-guide.md](references/version-catalog-guide.md) for comprehensive patterns.

Common Build Tasks

Quick Reference

# Full builds
./gradlew build                    # All modules
./gradlew clean build              # Clean build

# Desktop
./gradlew :desktopApp:run          # Run desktop app
./gradlew :desktopApp:packageDmg   # macOS package

# Module-specific
./gradlew :quartz:build            # KMP library only
./gradlew :commons:build           # Shared UI only

# Analysis
./gradlew dependencies             # Dependency tree
./gradlew build --scan             # Online diagnostics

See [references/build-commands.md](references/build-commands.md) for comprehensive command reference.

Module Structure & Dependencies

Dependency Flow

**Desktop build chain:**

:desktopApp → :commons (jvmMain) → :quartz (jvmMain → jvmAndroid → commonMain)

**Android build chain:**

:amethyst → :commons (androidMain) → :quartz (androidMain → jvmAndroid → commonMain)

**Key source set pattern (quartz & commons):**

commonMain           # Truly cross-platform code
    │
    ├─ jvmAndroid    # JVM-specific, shared by Android + Desktop
    │   ├─ androidMain
    │   └─ jvmMain
    │
    └─ iosMain       # iOS-specific (quartz only)

**Dependency config types:**

  • Use `api` when types appear in module's public API or expect/actual declarations
  • Use `implementation` for internal implementation details
  • Example: quartz exposes secp256k1 (`api`), but hides okhttp (`implementation`)

See [references/dependency-graph.md](references/dependency-graph.md) for module visualization and transitive dependency flow.

Critical Dependency Patterns

1. secp256k1 (Crypto Library)

**The problem:** KMP library with platform-specific JNI bindings. Wrong variant = runtime crash.

**Pattern:**

// commonMain - API only
api(libs.secp256k1.kmp.common)

// androidMain - Android JNI
api(libs.secp256k1.kmp.jni.android)

// jvmMain - Desktop JVM JNI
implementation(libs.secp256k1.kmp.jni.jvm)

**Why api in androidMain?** Types leak to consumers (:amethyst).

**Common error:** Desktop using jni-android variant → `UnsatisfiedLinkError: no secp256k1jni in java.library.path`

**Fix:** Check source set dependencies. jvmMain must use jni-jvm, never jni-android.

2. JNA (for LibSodium Encryption)

**The problem:** Android needs AAR packaging, JVM needs JAR. Same library, different artifact types.

**Pattern:**

// androidMain
implementation("com.goterl:lazysodium-android:5.2.0@aar")  // @aar explicit
implementation("net.java.dev.jna:jna:5.18.1@aar")

// jvmMain
implementation(libs.lazysodium.java)  // JAR implicit
implementation(libs.jna)

**Critical:** Never put JNA in jvmAndroid or commonMain. Platform-specific packaging only.

3. Compose Versions

**The problem:** Two Compose ecosystems (Multiplatform + AndroidX) must align, or duplicate classes.

**Current project config** (always re-check `gradle/libs.versions.toml` — these drift):

composeMultiplatform = "1.11.1"  # Plugin + runtime
composeBom = "2026.05.01"        # AndroidX Compose BOM
kotlin = "2.3.21"

**Ru

Read more
Ships withamethyst

Nostr client for Android

Get the whole plugin

Other skills on amethyst.