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) 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).

1,495 stars

Best use case

gradle-expert is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

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).

Teams using gradle-expert should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/gradle-expert/SKILL.md --create-dirs "https://raw.githubusercontent.com/vitorpamplona/amethyst/main/.claude/skills/gradle-expert/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/gradle-expert/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How gradle-expert Compares

Feature / Agentgradle-expertStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

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).

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Gradle Expert

Build system expertise for AmethystMultiplatform's 4-module KMP architecture. Focus: practical troubleshooting, dependency resolution, and project-specific optimizations.

## Build Architecture Mental Model

Think of this project as **4 layers**:

```
┌─────────────┬─────────────┐
│ :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:**
```toml
[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:**
```kotlin
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

```bash
# 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:**
```kotlin
// 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:**
```kotlin
// 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:**
```toml
composeMultiplatform = "1.9.3"  # Plugin + runtime
composeBom = "2025.12.01"       # AndroidX Compose BOM
kotlin = "2.3.0"
```

**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):**
```kotlin
// ✅ 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):**
```kotlin
// Can use AndroidX BOM
val composeBom = platform(libs.androidx.compose.bom)
implementation(composeBom)
```

## Desktop Packaging Basics

**TargetFormat options:**
```kotlin
// 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:**
```bash
./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`:**
```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:**
```bash
./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:**
```bash
./gradlew dependencyInsight --dependency <library-name>
```

**Fix options:**
1. Align versions in libs.versions.toml (preferred)
2. Force resolution:
```kotlin
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.

```kotlin
// ❌ 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`:
```proguard
# 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 class file major version 65`

**Fix:** Ensure Java 21 everywhere:
```bash
# Check current Java
java -version  # Should show 21

# Set JAVA_HOME
export JAVA_HOME=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home

# Stop Gradle daemon to pick up new Java
./gradlew --stop
```

Verify all build files use JVM 21:
```kotlin
kotlin {
    jvm {
        compilerOptions {
            jvmTarget.set(JvmTarget.JVM_21)
        }
    }
}

android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_21
        targetCompatibility = JavaVersion.VERSION_21
    }
}
```

## Comprehensive Error Guide

For detailed troubleshooting of specific errors, see [references/common-errors.md](references/common-errors.md). Covers:
- Compose version conflicts
- secp256k1 JNI errors
- Source set dependency issues
- Proguard/R8 problems
- Desktop packaging errors
- Kotlin compilation errors
- Dependency resolution failures
- JVM/JDK version issues

Each error includes: symptom, cause, solution, verification steps.

## Quick Diagnostic Commands

```bash
# Check dependencies for specific module
./gradlew :quartz:dependencies

# Find specific library in dependency tree
./gradlew dependencyInsight --dependency okhttp

# Build with detailed logging
./gradlew build --info

# Generate interactive build scan (best diagnostics)
./gradlew build --scan

# Profile build performance
./gradlew clean build --profile

# Stop all Gradle daemons (fresh start)
./gradlew --stop

# Check Gradle version
./gradlew --version
```

## Scripts & References

### Diagnostic Scripts
- `scripts/analyze-build-time.sh` - Profile build performance, generate optimization report
- `scripts/fix-dependency-conflicts.sh` - Diagnose common dependency conflicts, suggest fixes

### Reference Docs
- `references/build-commands.md` - Comprehensive command reference for all tasks
- `references/dependency-graph.md` - Module dependencies, source set hierarchy, transitive deps
- `references/version-catalog-guide.md` - Version catalog patterns, usage, best practices
- `references/common-errors.md` - Troubleshooting guide for frequent build issues

## Workflow Examples

### Example 1: Adding New Dependency

**Task:** Add kotlinx.datetime to quartz

**Steps:**
1. **Update version catalog** (gradle/libs.versions.toml):
```toml
[versions]
kotlinxDatetime = "0.6.0"

[libraries]
kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinxDatetime" }
```

2. **Add to build file** (quartz/build.gradle.kts):
```kotlin
sourceSets {
    commonMain {
        dependencies {
            implementation(libs.kotlinx.datetime)  // KMP library, goes in commonMain
        }
    }
}
```

3. **Sync & verify:**
```bash
./gradlew :quartz:dependencies | grep datetime
```

### Example 2: Fixing secp256k1 Error on Desktop

**Error:** `UnsatisfiedLinkError: no secp256k1jni in java.library.path` when running desktop app

**Diagnosis:**
```bash
./gradlew :desktopApp:dependencies --configuration runtimeClasspath | grep secp256k1
# Shows: secp256k1-kmp-jni-android  ← WRONG!
```

**Fix:**
```kotlin
// In quartz/build.gradle.kts
jvmMain {
    dependencies {
        // Change from:
        // implementation(libs.secp256k1.kmp.jni.android)  ❌

        // To:
        implementation(libs.secp256k1.kmp.jni.jvm)  // ✅
    }
}
```

**Verify:**
```bash
./gradlew :desktopApp:dependencies --configuration runtimeClasspath | grep secp256k1
# Now shows: secp256k1-kmp-jni-jvm  ✅

./gradlew :desktopApp:run  # Should work
```

### Example 3: Optimizing Build Time

**Current:** Clean build takes 5 minutes

**Steps:**
1. **Baseline measurement:**
```bash
./gradlew clean build --profile
# Check: build/reports/profile/profile-*.html
```

2. **Add optimizations** to gradle.properties:
```properties
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true
org.gradle.jvmargs=-Xmx4g
kotlin.incremental=true
```

3. **Re-measure:**
```bash
./gradlew clean build --profile
```

**Expected improvement:** 30-50% faster on subsequent builds (incremental builds much faster).

## Delegation Patterns

**When to delegate to other skills:**
- **Source set architecture** (jvmAndroid pattern, expect/actual) → Use `kotlin-multiplatform` skill
- **Compose UI issues** (composables, state management) → Use `compose-expert` skill (when available)
- **Kotlin language issues** (Flow, sealed classes, DSLs) → Use `kotlin-expert` skill
- **Desktop-specific features** (Window management, MenuBar, tray) → Use `desktop-expert` skill

**This skill handles:** Build system, dependencies, versioning, module structure, packaging, performance.

## Core Principles for This Build System

1. **Centralize versions:** Never hardcode versions in build.gradle.kts. Always use libs.versions.toml.

2. **Respect source set hierarchy:** Dependencies flow downward. jvmAndroid depends on commonMain, never the reverse.

3. **Platform-specific variants matter:** secp256k1, JNA must use correct variant per platform. Check when errors occur.

4. **Clean builds are expensive:** Use incremental compilation. Only clean when truly needed (source set changes, version updates).

5. **Compose alignment is critical:** Compose Multiplatform version must match Kotlin version. Check compatibility matrix.

6. **Proguard for native libs:** All JNI libraries need explicit `-keep` rules in release builds.

7. **Java 21 everywhere:** All modules, all targets, consistent JVM version.

Related Skills

nostr-expert

1495
from vitorpamplona/amethyst

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.

kotlin-expert

1495
from vitorpamplona/amethyst

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.

Desktop Expert

1495
from vitorpamplona/amethyst

Expert in Compose Multiplatform Desktop development for AmethystMultiplatform. Covers Desktop-specific APIs, OS conventions, navigation patterns, and UX principles.

compose-expert

1495
from vitorpamplona/amethyst

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).

android-expert

1495
from vitorpamplona/amethyst

Android platform expertise for Amethyst Multiplatform project. Covers Compose Navigation, Material3, permissions, lifecycle, and Android-specific patterns in KMP architecture.

Amethyst Builder Skill

1495
from vitorpamplona/amethyst

Build customized Amethyst Nostr clients for Android. Fork, rebrand, customize, and distribute your own version.

quartz-integration

1495
from vitorpamplona/amethyst

Integration guide for using the Quartz Nostr KMP library in external projects. Use when: (1) adding Quartz as a Gradle dependency, (2) setting up NostrClient with WebSocket, (3) creating/signing/sending events, (4) building relay subscriptions with Filter, (5) handling keys with KeyPair/NostrSignerInternal, (6) using Bech32 encoding/decoding (NIP-19), (7) platform-specific setup (Android vs JVM/Desktop), (8) NIP-57 zaps, NIP-17 DMs, NIP-44 encryption in external projects.

kotlin-multiplatform

1495
from vitorpamplona/amethyst

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.

kotlin-coroutines

1495
from vitorpamplona/amethyst

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.

find-non-lambda-logs

1495
from vitorpamplona/amethyst

Use when auditing or migrating Log calls to lambda overloads, after adding new logging, or checking for string interpolation in Log.d/i/w/e calls that waste allocations when the log level is filtered out

find-missing-translations

1495
from vitorpamplona/amethyst

Use when comparing Android strings.xml locale files to find untranslated string resources, missing translation keys, or preparing translation work for a specific language

computer-vision-expert

31392
from sickn33/antigravity-awesome-skills

SOTA Computer Vision Expert (2026). Specialized in YOLO26, Segment Anything 3 (SAM 3), Vision Language Models, and real-time spatial analysis.