Fast dependency symbol search for Gradle Java/Kotlin projects
Index every class, method, and field from your resolved classpath,
then search them instantly — from the CLI or directly inside Claude Code.
Warning
This project is in alpha stage. APIs, CLI flags, index format, and configuration schema are subject to breaking changes without notice. Use at your own risk and expect rough edges. Bug reports and feedback are very welcome!
Coding agents (like Claude Code) working on Gradle Java/Kotlin projects repeatedly struggle with external library symbols — blindly crawling ~/.gradle/caches/, accessing artifacts outside the actual classpath, decompiling classes when source JARs exist, and rediscovering this fragile workflow from scratch every time.
classpath-surfer solves this by building a local Tantivy full-text index over all symbols in your resolved classpath. Agents (and humans) can search symbols and read source code instantly.
| Feature | Description | |
|---|---|---|
| 🔍 | Symbol search | Smart search with auto FQN detection, CamelCase token splitting, and prefix matching — filter by kind, dependency, access level, or classpath |
| ⚡ | Fast indexing | Auto-extract Gradle project classpath and index all symbols in seconds with parallel JAR parsing via rayon; incremental updates via GAV-level diff |
| 🌐 | Kotlin signatures | Decode @kotlin.Metadata protobuf to display native Kotlin signatures like suspend fun and data class |
| 📦 | JVM-language agnostic | Java, Kotlin, Scala, Groovy, Clojure — search symbols from dependencies written in any JVM language |
| 📄 | Source code lookup | Auto-focus on a symbol with surrounding context; source JARs when available, otherwise on-demand CFR/Vineflower decompilation — Kotlin shows original .kt source with a secondary decompiled Java view |
| 🤖 | AI agent integration | --agentic JSON output with classified exit codes for any AI agent; optional Claude Code plugin for slash-command skills |
Note
Pre-built binaries are currently available for Apple Silicon (aarch64) only. For other platforms or building from source, see CONTRIBUTING.md.
brew install rscarrera27/tap/classpath-surferThe binary is also installed as cpsurf — a short alias you can use anywhere in place of classpath-surfer:
cpsurf search symbol ImmutableList # same as: classpath-surfer search symbol ImmutableListcd your-gradle-project classpath-surfer index init # writes config, Gradle init script, then runs initial refresh # Verify the index was built classpath-surfer index status # → 38 dependencies, 77,219 symbols indexed, 2.1 MB on disk# Search by name classpath-surfer search symbol ImmutableList # CamelCase token matching — finds ImmutableList, ImmutableMap, ImmutableSet, etc. classpath-surfer search symbol Immutable # Find coroutine launchers in kotlinx-coroutines classpath-surfer search symbol launch --type method --dependency "org.jetbrains.kotlinx:*" # List all symbols in a specific dependency classpath-surfer search symbol --dependency "com.google.guava:guava" # Glob search for HTTP client classes classpath-surfer search symbol "Http*Client" --type class # Filter by classpath (compile, runtime, testCompile, testRuntime, ...) classpath-surfer search symbol Annotation --type class --classpath compile # FQN-like queries are auto-detected classpath-surfer search symbol com.google.common.collect.ImmutableList# Show source — auto-focuses on the symbol with 25 lines of context classpath-surfer show com.google.common.collect.ImmutableList classpath-surfer show com.google.common.collect.ImmutableList.of # Widen context / show the full file classpath-surfer show com.google.common.collect.ImmutableList --context 50 classpath-surfer show com.google.common.collect.ImmutableList --full # Kotlin sources display original .kt files (suspend fun, data class, etc.) classpath-surfer show kotlinx.coroutines.CoroutineScopeIf a -sources.jar is available it will be used; otherwise the class is decompiled with CFR (default) or Vineflower.
# List indexed dependencies with symbol counts and classpaths classpath-surfer search dep # Filter by GAV pattern classpath-surfer search dep "io.netty:*" # Show only runtime dependencies classpath-surfer search dep --classpath runtime# List indexed Java packages with symbol counts classpath-surfer search pkg # Filter by package pattern classpath-surfer search pkg "com.google.*" # Packages from a specific dependency classpath-surfer search pkg --dependency "com.google.code.gson:gson:*" # Filter by classpath classpath-surfer search pkg --classpath compile# All commands support --agentic for structured JSON output classpath-surfer search symbol ImmutableList --agentic classpath-surfer show com.google.common.collect.ImmutableList --agentic # Non-TTY automatically outputs plain text (pipe-friendly) classpath-surfer search symbol ImmutableList | head| Command | Description |
|---|---|
search symbol <query> | Search for symbols in the index |
search dep [pattern] | List indexed dependencies with symbol counts |
search pkg [pattern] | List indexed Java packages |
show <fqn> | Display source code for a symbol (focuses on the target symbol by default) |
index init | Install Gradle init script, default config, and run initial refresh |
index refresh | Extract classpath via Gradle and build/update the symbol index (skips Gradle when fresh; use --force to override) |
index status | Show index stats (dependency count, symbol count, staleness, disk size) |
index clean | Remove index data |
--agentic | Global flag: emit structured JSON output for AI agents and scripts |
Benchmarked on Macbook Pro 2023(M2 Pro, 32GB) with a 42-dependency project (156,116 symbols including Guava, Spring Core, Ktor, kotlinx-coroutines, OkHttp, and more):
| Query type | Latency |
|---|---|
Simple keyword (ImmutableList) | 872 µs |
| FQN exact match | 14 µs |
Glob (Immutable*) | 476 µs |
| With type filter | 848 µs |
| With dependency filter | 873 µs |
| Operation | Time |
|---|---|
| Full refresh (42 deps, 156K symbols) | 1.88 s |
| Incremental refresh (1 dep removed) | 638 ms |
| No-op refresh (up to date) | 177 µs |
Reproduce these benchmarks
cargo bench --bench search cargo bench --bench refreshgraph TD A["Gradle project\n(gradlew)"] -- "init script injects\nclasspathSurferExport task" --> B["Classpath\nmanifest.json"] B --> C1["JAR #1"] B --> C2["JAR #2"] B --> CN["JAR #N"] C1 -- "cafebabe .class\n+ Kotlin metadata" --> D C2 -- "parse symbols" --> D CN --> D D["Tantivy full-text index\nFQN · name · camelCase tokens"] D --> E1["search\n(TUI / plain / JSON)"] D --> E2["show\n(source)"] D --> E3["status\n(staleness)"] - Extract — A Gradle init script resolves
compileClasspathandruntimeClasspathfor every subproject, writing a per-module JSON manifest with each dependency's GAV coordinates and JAR paths (including source JARs when available). - Parse — Each JAR is opened with the
cafebabecrate. Every.classfile is parsed to extract class names, methods, fields, descriptors, and access flags. For Kotlin classes, the@kotlin.Metadataannotation is decoded via protobuf (prost) to produce Kotlin-native signatures. TheSourceFileattribute is used to detect the source language. - Index — Extracted symbols are written into a Tantivy index with fields for FQN, simple name, camelCase-split tokens, kind, signature, and GAV.
- Search — Queries hit the Tantivy index. Results are ranked by relevance and returned as a table or JSON.
- Staleness — On each search, the tool checks lockfile hashes and build-file mtimes against the snapshot taken at index time. If anything changed, it asks you to
index refresh.
classpath-surfer ships as a Claude Code plugin with three skills that mirror CLI commands:
| Skill | Usage |
|---|---|
/search-classpath <query> | Search for symbols, dependencies, or packages |
/show-classpath-source <fqn> | Show source code for a fully qualified symbol |
/classpath-index [action] | Manage the symbol index (init, refresh, status, clean) |
# Inside Claude Code /plugin marketplace add https://github.com/rscarrera27/classpath-surfer /plugin install classpath-surferThis lets Claude Code discover and read dependency APIs without you having to look them up manually.
classpath-surfer index init writes .classpath-surfer/config.json:
{ "decompiler": "cfr", "decompiler_jar": null, "configurations": ["compileClasspath", "runtimeClasspath"], "no_decompile": false, "gradle_timeout": null }| Field | Description |
|---|---|
decompiler | "cfr" or "vineflower" |
decompiler_jar | Explicit path to the decompiler JAR. If unset, reads CFR_JAR or VINEFLOWER_JAR env var |
configurations | Gradle configurations to resolve |
no_decompile | false (default). When true, fail instead of decompiling if no source JAR |
gradle_timeout | Gradle execution timeout in seconds. Default: 300 (5 minutes). Also settable via --timeout |
CLI flags (--decompiler, --configurations, --no-decompile) override config file values when provided.
- Gradle project with
gradlew(orgradleonPATH) - JDK (only needed for decompilation via
show)
For build-from-source requirements, see CONTRIBUTING.md.
Contributions are welcome! See CONTRIBUTING.md for details.
Licensed under the Apache License, Version 2.0.