GR

Provides support for Gradle build systems, including dependency resolution and performance optimization in KMP architectures.

Install

mkdir -p .claude/skills/gradle-expert && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/5381" && unzip -o skill.zip -d .claude/skills/gradle-expert && rm skill.zip

Installs to .claude/skills/gradle-expert

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

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).
524 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Advanced

Key capabilities

  • Manages dependency flow across 10 distinct modules
  • Configures jvmAndroid source sets for cross-platform sharing
  • Maintains central versioning via libs.versions.toml
  • Troubleshoots JNI variant conflicts for native libraries

How it works

Applies a predefined architecture mental model to force downward dependency flows and custom source set resolutions.

Inputs & outputs

You give it
Module path or dependency target
You get back
Optimized gradle configuration or build resolution

When to use gradle-expert

  • Resolving Gradle dependency conflicts
  • Optimizing KMP module build performance
  • Updating version catalogs
  • Configuring desktop packaging for Kotlin projects

About this skill

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 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 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 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"

Rule: Compose Multiplatform version must be compatible with Kotlin version. Check: https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-compatibility-and-versioning.html

In KMP modules (quartz, commons):

// ✅ Use Compose Multiplatform
implementation(compose.ui)
implementation(compose.material3)

// ❌ DON'T use AndroidX BOM in KMP modules
// implementation(libs.androidx.compose.bom)

In Android-only modules (amethyst):

// Can use AndroidX BOM
val composeBom = platform(libs.androidx.compose.bom)
implementation(composeBom)

Desktop Packaging Basics

TargetFormat options:

// In desktopApp/build.gradle.kts
nativeDistributions {
    targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)

    packageName = "Amethyst"
    packageVersion = "1.0.0"

    macOS {
        bundleID = "com.vitorpamplona.amethyst.desktop"
        iconFile.set(project.file("src/jvmMain/resources/icon.icns"))
    }
}

Package tasks:

./gradlew :desktopApp:packageDmg  # macOS
./gradlew :desktopApp:packageMsi  # Windows
./gradlew :desktopApp:packageDeb  # Linux

Output locations:

  • macOS: desktopApp/build/compose/binaries/main/dmg/
  • Windows: desktopApp/build/compose/binaries/main/msi/
  • Linux: desktopApp/build/compose/binaries/main/deb/

Icon requirements:

  • macOS: .icns (multi-resolution: 512, 256, 128, 32)
  • Windows: .ico (256, 128, 64, 32, 16)
  • Linux: .png (512x512)

Common issues:

  • Main class not found → Verify mainClass = "...MainKt" (Kotlin adds Kt suffix)
  • Native libs missing → Ensure secp256k1-kmp-jni-jvm in dependencies
  • Icon not found → Check file exists at path, use absolute path if needed

Build Performance Optimization

Add to gradle.properties:

# Daemon (faster subsequent builds)
org.gradle.daemon=true
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g

# Parallel execution (multi-module speedup)
org.gradle.parallel=true
org.gradle.workers.max=8

# Caching (incremental builds)
org.gradle.caching=true
org.gradle.configuration-cache=true

# Kotlin daemon
kotlin.incremental=true
kotlin.daemon.jvmargs=-Xmx2g

Impact: Typically 30-50% faster builds after first run.

Measure impact:

./gradlew clean build --profile
# Report: build/reports/profile/profile-<timestamp>.html

When to clean build:

  • After changing version catalog
  • After adding/removing source sets
  • When seeing unexplained errors

When NOT to clean:

  • Regular development iteration
  • Small code changes
  • Incremental compilation works fine

Use script: scripts/analyze-build-time.sh for automated profiling.

Troubleshooting: Practical Patterns

Pattern 1: Version Conflict

Symptom: Duplicate class or NoSuchMethodError

Diagnosis:

./gradlew dependencyInsight --dependency <library-name>

Fix options:

  1. Align versions in libs.versions.toml (preferred)
  2. Force resolution:
configurations.all {
    resolutionStrategy {
        force(libs.okhttp.get().toString())
    }
}

Pattern 2: Source Set Issues

Symptom: Unresolved reference to JVM library in shared code

Diagnosis: Check source set hierarchy. JVM-only libs (jackson, okhttp) can't be in commonMain.

Fix: Move to jvmAndroid or platform-specific source set.

// ❌ Wrong
commonMain {
    dependencies {
        implementation(libs.jackson.module.kotlin)  // JVM-only!
    }
}

// ✅ Correct
val jvmAndroid = create("jvmAndroid") {
    dependsOn(commonMain.get())
    dependencies {
        api(libs.jackson.module.kotlin)  // JVM code, shared by Android + Desktop
    }
}

Pattern 3: Proguard Stripping Native Libs

Symptom: NoClassDefFoundError for secp256k1, JNA, or LibSodium in release builds

Fix: Update proguard rules in quartz/proguard-rules.pro:

# Native libraries
-keep class fr.acinq.secp256k1.** { *; }
-keep class com.goterl.lazysodium.** { *; }
-keep class com.sun.jna.** { *; }

# Jackson (reflection-based)
-keep class com.vitorpamplona.quartz.** { *; }
-keepattributes *Annotation*
-keepattributes Signature

Pattern 4: Compose Compiler Mismatch

Symptom: IllegalStateException: Version mismatch: runtime 1.10.0 but compiler 1.9.0

Fix: Update Compose Multiplatform version in libs.versions.toml to match Kotlin version compatibility.

Check: https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-compatibility-and-versioning.html

Pattern 5: Wrong JVM Target

Symptom: `Unsupported cla


Content truncated.

When not to use it

  • When working with non-KMP Kotlin projects
  • When modifying simple Android-only apps without complex dependencies

Prerequisites

gradleKotlin Multiplatform environment

Limitations

  • Strict dependency rules may prevent flexible module reordering
  • Over-optimization of build scripts may complicate project maintenance

How it compares

It uses project-specific 'jvmAndroid' patterns unique to the Amethyst multi-module setup.

Compared to similar skills

gradle-expert side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
gradle-expert (this skill)12moReviewAdvanced
debug03moReviewIntermediate
gh-fix-ci126moReviewIntermediate
fix-ci16moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

More by vitorpamplona

View all by vitorpamplona

kotlin-multiplatform

vitorpamplona

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) with web/wasm future considerations. 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.

32156

compose-expert

vitorpamplona

Advanced Compose Multiplatform UI patterns for shared composables. Use when working with visual UI components, state management patterns (remember, derivedStateOf, produceState), recomposition optimization (@Stable/@Immutable visual usage), Material3 theming, custom ImageVector icons, or determining whether to share UI in commonMain vs keep platform-specific. Delegates navigation to android-expert/desktop-expert. Complements kotlin-expert (handles Kotlin language aspects of state/annotations).

632

kotlin-expert

vitorpamplona

Advanced Kotlin patterns for AmethystMultiplatform. Flow state management (StateFlow/SharedFlow), sealed hierarchies (classes vs interfaces), immutability (@Immutable, data classes), DSL builders (type-safe fluent APIs), inline functions (reified generics, performance). Use when working with: (1) State management patterns (StateFlow/SharedFlow/MutableStateFlow), (2) Sealed classes or sealed interfaces, (3) @Immutable annotations for Compose, (4) DSL builders with lambda receivers, (5) inline/reified functions, (6) Kotlin performance optimization. Complements kotlin-coroutines agent (async patterns) - this skill focuses on Amethyst-specific Kotlin idioms.

526

kotlin-coroutines

vitorpamplona

Advanced Kotlin coroutines patterns for AmethystMultiplatform. Use when working with: (1) Structured concurrency (supervisorScope, coroutineScope), (2) Advanced Flow operators (flatMapLatest, combine, merge, shareIn, stateIn), (3) Channels and callbackFlow, (4) Dispatcher management and context switching, (5) Exception handling (CoroutineExceptionHandler, SupervisorJob), (6) Testing async code (runTest, Turbine), (7) Nostr relay connection pools and subscriptions, (8) Backpressure handling in event streams. Delegates to kotlin-expert for basic StateFlow/SharedFlow patterns. Complements nostr-expert for relay communication.

311

nostr-expert

vitorpamplona

Nostr protocol implementation patterns in Quartz (AmethystMultiplatform's KMP Nostr library). Use when working with: (1) Nostr events (creating, parsing, signing), (2) Event kinds and tags, (3) NIP implementations (57 NIPs in quartz/), (4) Event builders and TagArrayBuilder DSL, (5) Nostr cryptography (secp256k1, NIP-44 encryption), (6) Relay communication patterns, (7) Bech32 encoding (npub, nsec, note, nevent). Complements nostr-protocol agent (NIP specs) - this skill provides Quartz codebase patterns and implementation details.

323

Search skills

Search the agent skills registry