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
Stats
1,600
Stars
221
Forks
Active
Maintenance
Kotlin
Language
MIT
License
41m ago
Last commit
3y ago
Created

Repo: vitorpamplona/amethyst

Other skills on amethyst.