# jdx — full documentation > jdx is an IDE for AI agents, as a command line tool: members, method bodies, usages, type hierarchies and call graphs for any JVM jar, Maven coordinate or the JDK — one precise answer per command. Source: https://mohammadmd1383.github.io/jdx/ — generated from https://github.com/MohammadMD1383/jdx; examples are real jdx output. --- # jdx — an IDE for AI agents [![Latest Release](https://img.shields.io/github/v/release/MohammadMD1383/jdx)](https://github.com/MohammadMD1383/jdx/releases) [![Linux CI](https://github.com/MohammadMD1383/jdx/actions/workflows/linux.yml/badge.svg)](https://github.com/MohammadMD1383/jdx/actions/workflows/linux.yml) [![macOS CI](https://github.com/MohammadMD1383/jdx/actions/workflows/macos.yml/badge.svg)](https://github.com/MohammadMD1383/jdx/actions/workflows/macos.yml) [![Windows CI](https://github.com/MohammadMD1383/jdx/actions/workflows/windows.yml/badge.svg)](https://github.com/MohammadMD1383/jdx/actions/workflows/windows.yml) [![Release CI](https://github.com/MohammadMD1383/jdx/actions/workflows/release.yml/badge.svg)](https://github.com/MohammadMD1383/jdx/actions/workflows/release.yml) [![License](https://img.shields.io/github/license/MohammadMD1383/jdx)](https://github.com/MohammadMD1383/jdx/blob/main/LICENSE) [![M8ven Score](https://m8ven.ai/badge/mcp/mohammadmd1383/jdx)](https://m8ven.ai/mcp/mohammadmd1383/jdx) **Website \& docs: [mohammadmd1383.github.io/jdx](https://mohammadmd1383.github.io/jdx/)** ## Try it in 30 seconds ```bash curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash jdx members java.util.HashMap --limit 5 # MCP (Claude Code / Cursor): {"mcpServers":{"jdx":{"command":"jdx","args":["mcp"]}}} # MCP (OpenCode v1+v2): jdx setup --agent opencode --scope project (then restart opencode) # MCP (Claude Code): jdx setup --agent claude-code --scope project (then restart claude) # MCP (Cursor): jdx setup --agent cursor --scope project (then restart Cursor) # MCP (Kilo Code): jdx setup --agent kilo --scope project (then restart Kilo Code) # MCP (Cline): jdx setup --agent cline --scope system (then restart Cline) # MCP (Codex CLI): jdx setup --agent codex --scope project (then restart codex) # MCP (GitHub Copilot CLI): jdx setup --agent copilot --scope project (then restart copilot) # MCP (Antigravity): jdx setup --agent antigravity --scope project (then restart Antigravity) ``` --- An AI agent working on a JVM codebase spends most of its time — and most of its context window — answering questions a human answers in an IDE with one keystroke: *What methods does this class have? What does this method actually do? Who calls it? What implements this interface?* Today an agent answers those with `unzip`, `javap`, `grep`, and by reading 1,400-line source files into its context. It is slow, noisy, and expensive, and `javap` gives it erased signatures and raw opcodes. `jdx` answers them directly — from `.jar`, `-sources.jar`, class directories, source directories, Maven coordinates, and the JDK itself: ```console $ jdx members java.util.HashMap --limit 5 members of java.util.HashMap constructors declared on java.util.HashMap: constructor public HashMap() constructor public HashMap(int initialCapacity) ... 5 of 54 members shown (--limit 54 to see more) source: java.base (jrt) $ jdx body 'java.util.HashMap#get(java.lang.Object)' java.util.HashMap#get(java.lang.Object) source: decompiled by vineflower from java.base · java/util/HashMap.java:178-182 ⚠ reconstructed @Override public V get(Object key) { Node e; return (e = this.getNode(key)) == null ? null : e.value; } ``` No sources jar? It decompiles with Vineflower — and **says so**, so the agent knows it is reading a reconstruction (pass `--engine javap` for raw opcodes instead). > **Proof, not promises.** One real task — *list Gson's API, then show only > `toJson(Object)`* — measured on gson-2.14.0: > > |Approach|calls|bytes in context (~tokens)| > |---|---|---| > |`unzip` + `javap` + `grep` + `sed`|5|~17.5 KB (~4.4k) — and `head -200` misses the method (line 542 of 1265)| > |`jdx members` + `jdx body`|**2**|**~4.6 KB (~1.2k)** — exact method, 267 bytes (~67 tokens)| > > The body alone: **267 bytes vs 60,590 (`javap -c`, 226×) vs 58,251 (full > `Gson.java`, 218×)**. Tokens ≈ bytes/4. Reproduce it: `sh docs/bench-compare.sh`. Same task, two agents — left does it with `javap`/`grep`, right with `jdx`: ![baseline vs jdx: find all System.exit call sites in java.base](https://mohammadmd1383.github.io/jdx/assets/demo-callsites.gif)
More side-by-side demos (method body · implementors · Maven coordinates) ![baseline vs jdx: show only the body of HashMap#get](https://mohammadmd1383.github.io/jdx/assets/demo-method-body.gif) ![baseline vs jdx: who implements java.util.Map](https://mohammadmd1383.github.io/jdx/assets/demo-implementors.gif) ![baseline vs jdx: inspect Gson from a Maven coordinate](https://mohammadmd1383.github.io/jdx/assets/demo-maven-deps.gif)
## Install Requires **JDK 21+**. Every push of a `vX.Y.Z` tag publishes per-OS bundles as [GitHub Releases](https://github.com/MohammadMD1383/jdx/releases) — `jdx--.tar.gz` and `jdx--.zip` (`` is `linux`, `macos`, or `windows`), each with a `.sha256` checksum and a `jdx.jsa` trained on that OS: ```bash curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash ``` Installs per-user into `~/.local/share/jdx` and links `~/.local/bin/jdx`. Prefer the `.zip` on Windows (Explorer/Defender are zip-first) — download the `-windows.zip` asset and install it with `sh install-release.sh --tarball jdx--windows.zip`. Verify with `jdx version` (`jdx doctor` reports JDK, cache, index, Kotlin sidecar, AppCDS archive, daemon, and workspace status). Release installs self-update with `jdx upgrade`. Building from source: `./gradlew :app:installDist && ./install.sh` (see [`CONTRIBUTING.md`](https://mohammadmd1383.github.io/jdx/docs/contributing/)). > Releases are **unsigned**: Windows SmartScreen and macOS Gatekeeper will warn > on first run. `Unblock-File jdx.bat` (PowerShell) or > `xattr -d com.apple.quarantine /jdx` clears it. Details and the > asset table live in `docs/PROPOSAL.md` §17.2. ## Quickstart ```bash # No setup: the running JDK is always on the classpath via jrt:/ jdx members java.util.HashMap --inherited --limit 10 jdx body 'com.google.gson.Gson#toJson(Object)' --jars gson.jar # Name a classpath once, reuse it everywhere (incl. daemon/MCP/HTTP/batch) jdx ws create fx --jars 'libs/*.jar' --src ./src jdx -w fx usages 'com.example.Service#run()' # Maven coordinates resolve from ~/.gradle/caches + ~/.m2 first; # --fetch downloads from Maven Central (with -sources.jar) when allowed jdx show com.google.gson.Gson --coord com.google.code.gson:gson:2.14.0 --fetch # Token budgets for the biggest listings (text only; --json bytes are unaffected) jdx members java.util.HashMap --inherited --brief --max-lines 20 # Warm path: start once, query at ~20 ms instead of ~250 ms cold jdx daemon start -w fx jdx -w fx members 'com.example.Service' # served warm automatically # One process, many queries echo '{"command":"show","query":"java.util.Map"}' | jdx batch --jars gson.jar # Upgrading a dependency? Ask what actually breaks jdx diff gson-2.11.0.jar gson-2.14.0.jar --fail-on breaking ``` ## What makes it an *IDE*, not a better `javap` - **Inherited members resolved transitively**, with generic substitution — the agent never walks a class hierarchy by hand again. - **A real index**, so *find usages*, *implementors*, and *call hierarchy* work across an entire classpath, not just one jar. - **Sources when they exist, decompilation when they don't** — uniformly, behind one command. - **True Kotlin signatures** from `@Metadata`: `suspend`, properties, default arguments, `@JvmName` mappings — not the misleading JVM projection. - **Javadoc/KDoc**, including documentation inherited from a supertype. - **`jdx samples`** — real call sites from the classpath as usage examples. Evidence instead of recollection. - **`jdx diff a.jar b.jar`** — the public-API diff for a dependency upgrade, with every difference named as a rule (`MEMBER_REMOVED`, `RETURN_TYPE_CHANGED`, …) at `breaking`/`suspicious`/`info`, and a `--fail-on` gate for CI. ## What makes it *for agents*, not for humans - **Minimal correct output.** Bounded, with an explicit hint for getting the rest. Never a file dump when a method was asked for. - **Never guesses.** An ambiguous reference exits `2` and lists every candidate as a copy-pasteable reference. - **Machine-legible outcomes.** Distinct exit codes for *not found*, *ambiguous*, *no index*, *bad usage* — branch without parsing prose. `--json` for everything. - **Provenance on every answer** — which jar, sources or bytecode, which decompiler. - **`jdx batch`** — many queries, one process, one index load. - **Four front-ends over one engine:** one-shot CLI, MCP server, a daemon, and a local HTTP/JSON API. - **`jdx help --agent`** — a compact paste-ready block for `CLAUDE.md` / system prompts. ## Full command reference The catalogue lives in [`docs/COMMANDS.md`](https://mohammadmd1383.github.io/jdx/docs/reference/) — every command, flag, exit code, and the symbol-reference grammar. `jdx --help` documents each command; `jdx help --agent` prints the compact agent block. ## Known limitations (by design) Graph queries re-scan live bytecode roots per query (no persistent-index acceleration yet); call hierarchy is exact name+descriptor matching, not override-aware. Full list in [GitHub Issues](https://github.com/MohammadMD1383/jdx/issues). Out-of-scope for v1 (mappings/remap, resources inspection, …) is the phase-2 backlog, also tracked there. ## Documentation ||| |---|---| |[Website](https://mohammadmd1383.github.io/jdx/)|Guides, generated command and MCP reference, integrations, FAQ| |[`docs/guide/`](https://mohammadmd1383.github.io/jdx/docs/introduction/)|User guides: install, quickstart, AI agents, classpath, reading and navigating code, API diff| |[`docs/COMMANDS.md`](https://mohammadmd1383.github.io/jdx/docs/reference/)|Full command reference (flags, exit codes, symbol grammar)| |[`AGENTS.md`](https://mohammadmd1383.github.io/jdx/docs/agents-md/) (+ per-module `AGENTS.md`)|Contributor notes: architecture rules, gotchas, build/test| |[`docs/PROPOSAL.md`](https://mohammadmd1383.github.io/jdx/docs/design/)|Full design specification (Appendix B is the flag reference)| |[`docs/TESTING.md`](https://mohammadmd1383.github.io/jdx/docs/testing/)|Testing strategy — required reading before writing tests| |[`CONTRIBUTING.md`](https://mohammadmd1383.github.io/jdx/docs/contributing/)|Conventions, code style, definition of done| |[GitHub Issues](https://github.com/MohammadMD1383/jdx/issues)|Limitations, caveats, and the phase-2 backlog| ## Contributing Contributors here — human and AI — do not share memory. Read [`CONTRIBUTING.md`](https://mohammadmd1383.github.io/jdx/docs/contributing/) before your first change; it is short and it explains the documentation discipline that keeps the project resumable by anyone. ## License Apache-2.0 (see `LICENSE`). Bundled third-party components and their licenses are listed in `docs/PROPOSAL.md` §19 and will be reproduced in `NOTICE`. --- # Documentation Start at the top and go as deep as you need: getting started takes five minutes, the guides cover every task, the reference is generated from the binary itself, and the advanced section is the full design. ## Getting started What jdx is, how to install it, your first queries, and wiring it into your AI agent. - [Introduction](https://mohammadmd1383.github.io/jdx/docs/introduction/): jdx is an IDE for AI agents, as a command line tool. It answers the questions an IDE answers for a human — what members a class has, what a method does, who… - [Installation](https://mohammadmd1383.github.io/jdx/docs/installation/): jdx installs per user with one command, needs only a Java 21+ runtime, and runs on Linux, macOS and Windows. - [Quickstart](https://mohammadmd1383.github.io/jdx/docs/quickstart/): Five minutes, two classpaths: the JDK (no setup at all) and a library pulled by its Maven coordinate. Every output below is real — it was produced by… - [Use with AI agents](https://mohammadmd1383.github.io/jdx/docs/ai-agents/): Wire jdx into Claude Code, Cursor, Codex, GitHub Copilot, Antigravity, OpenCode, Kilo Code, Cline or any MCP client with one command — or let any… ## Guides Task-oriented guides: naming symbols, choosing a classpath, reading and navigating code, upgrades, output, and serving. - [Symbol references](https://mohammadmd1383.github.io/jdx/docs/symbol-references/): Every jdx command takes a symbol reference in Javadoc style. jdx is generous about what it accepts and canonical about what it prints, so any reference it… - [Classpath & workspaces](https://mohammadmd1383.github.io/jdx/docs/classpath/): jdx answers from a classpath made of jars, class directories, source directories, Maven coordinates and the JDK. This guide covers every way to supply one —… - [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/): Look at a type, list what you can call on it, read one method, or search the whole classpath — each with one command that returns only what was asked. This… - [Usages, hierarchy & calls](https://mohammadmd1383.github.io/jdx/docs/navigating-code/): Find who uses a symbol, what implements a type, who calls a method and what it calls, and real call sites to learn from — across every jar on the classpath,… - [API diff for upgrades](https://mohammadmd1383.github.io/jdx/docs/api-diff/): Before bumping a dependency, ask what the new version breaks. jdx diff compares the public API of two artifacts — jars, class directories or Maven… - [Output, JSON & exit codes](https://mohammadmd1383.github.io/jdx/docs/output/): jdx output is designed to be read by a language model with a finite context window: compact text by default, a complete JSON envelope on request, and exit… - [MCP, daemon, HTTP & batch](https://mohammadmd1383.github.io/jdx/docs/serving/): The one-shot CLI starts in about a quarter of a second. When an agent asks dozens of questions, keep jdx warm instead: an MCP server, a background daemon, a… ## Reference Every command and flag, generated from the jdx binary itself on every build. - [Command catalogue](https://mohammadmd1383.github.io/jdx/docs/reference/): Every jdx command with its IDE equivalent and key flags: read, search, graph, API diff, workspaces, serving. - [Agent cheat sheet](https://mohammadmd1383.github.io/jdx/docs/reference/cheat-sheet/): The compact jdx block to paste into CLAUDE.md, AGENTS.md or a system prompt: every command, the ref syntax and the exit codes. - [MCP tools](https://mohammadmd1383.github.io/jdx/docs/reference/mcp-tools/): Every tool the jdx MCP server exposes to Claude Code, Cursor, Codex and other MCP clients, with parameters — generated from jdx mcp. Commands: [version](https://mohammadmd1383.github.io/jdx/docs/reference/version/), [upgrade](https://mohammadmd1383.github.io/jdx/docs/reference/upgrade/), [doctor](https://mohammadmd1383.github.io/jdx/docs/reference/doctor/), [show](https://mohammadmd1383.github.io/jdx/docs/reference/show/), [members](https://mohammadmd1383.github.io/jdx/docs/reference/members/), [outline](https://mohammadmd1383.github.io/jdx/docs/reference/outline/), [body](https://mohammadmd1383.github.io/jdx/docs/reference/body/), [source](https://mohammadmd1383.github.io/jdx/docs/reference/source/), [signature](https://mohammadmd1383.github.io/jdx/docs/reference/signature/), [doc](https://mohammadmd1383.github.io/jdx/docs/reference/doc/), [search](https://mohammadmd1383.github.io/jdx/docs/reference/search/), [resolve](https://mohammadmd1383.github.io/jdx/docs/reference/resolve/), [ls](https://mohammadmd1383.github.io/jdx/docs/reference/ls/), [tree](https://mohammadmd1383.github.io/jdx/docs/reference/tree/), [usages](https://mohammadmd1383.github.io/jdx/docs/reference/usages/), [hierarchy](https://mohammadmd1383.github.io/jdx/docs/reference/hierarchy/), [implementors](https://mohammadmd1383.github.io/jdx/docs/reference/implementors/), [callers](https://mohammadmd1383.github.io/jdx/docs/reference/callers/), [calls](https://mohammadmd1383.github.io/jdx/docs/reference/calls/), [samples](https://mohammadmd1383.github.io/jdx/docs/reference/samples/), [diff](https://mohammadmd1383.github.io/jdx/docs/reference/diff/), [batch](https://mohammadmd1383.github.io/jdx/docs/reference/batch/), [bench](https://mohammadmd1383.github.io/jdx/docs/reference/bench/), [serve](https://mohammadmd1383.github.io/jdx/docs/reference/serve/), [setup](https://mohammadmd1383.github.io/jdx/docs/reference/setup/), [mcp](https://mohammadmd1383.github.io/jdx/docs/reference/mcp/), [help](https://mohammadmd1383.github.io/jdx/docs/reference/help/), [daemon](https://mohammadmd1383.github.io/jdx/docs/reference/daemon/), [ws](https://mohammadmd1383.github.io/jdx/docs/reference/ws/), [cache](https://mohammadmd1383.github.io/jdx/docs/reference/cache/), [kotlin](https://mohammadmd1383.github.io/jdx/docs/reference/kotlin/) ## Advanced The full design specification, the testing strategy, and how this agent-built project is run. - [Design specification](https://mohammadmd1383.github.io/jdx/docs/design/): The full jdx design: symbol grammar, command surface, output design, index, decompilation, Kotlin support, serving, security. - [Testing strategy](https://mohammadmd1383.github.io/jdx/docs/testing/): How jdx is tested: TDD core, javap-differential, property-based, metamorphic, fault-injection and corpus soak tests, mutation gates. - [Built by agents](https://mohammadmd1383.github.io/jdx/docs/built-by-agents/): jdx is a tool for AI agents that is itself written almost entirely by AI agents, working under rules a human owner sets. This page describes how that works… - [AGENTS.md](https://mohammadmd1383.github.io/jdx/docs/agents-md/): The rules every contributor — human or AI agent — follows in the jdx repository: principles, locked decisions, exit codes, build and test gates. - [Contributing](https://mohammadmd1383.github.io/jdx/docs/contributing/): How to contribute to jdx: conventions, code style, the command checklist, testing and documentation discipline. --- # Introduction jdx is an IDE for AI agents, as a command line tool. It answers the questions an IDE answers for a human — what members a class has, what a method does, who calls it, what implements an interface, what an upgrade breaks — for any JVM jar, Maven coordinate, class or source directory, and the JDK itself. ## Why it exists An agent working on a Java or Kotlin codebase constantly needs to know things about code it cannot see: the library jars on the classpath and the JDK. Without jdx it pieces the answers together with `unzip`, `javap`, `grep` and by reading thousand-line source files into its context window. That is slow, noisy, and burns the context the agent needs for the actual task. jdx replaces each of those multi-step hunts with one command that returns one precise answer: ```console jdx-generated $ jdx show java.util.HashMap class java.util.HashMap public class HashMap extends java.util.AbstractMap implements java.util.Map, java.lang.Cloneable, java.io.Serializable members: 4 constructors, 52 methods, 13 fields source: java.base (jrt) next: jdx members java.util.HashMap --inherited ``` Every answer names its symbols canonically and suggests the next command (`next:`), so an agent moves from question to question by copy-paste instead of reconstruction. ## What it can answer |Question|Command|IDE gesture| |---|---|---| |What is this type?|`jdx show`|Go to declaration| |What can I call on it?|`jdx members`|Code completion after `.`| |What does this method do?|`jdx body`|Open the method| |What does the javadoc say?|`jdx doc`|Quick documentation| |Who uses this?|`jdx usages`|Find usages| |Who implements this?|`jdx hierarchy`, `jdx implementors`|Type hierarchy| |Who calls this, what does it call?|`jdx callers`, `jdx calls`|Call hierarchy| |How is it used in practice?|`jdx samples`|*(no IDE equivalent)*| |What does this upgrade break?|`jdx diff`|*(no IDE equivalent)*| The [command catalogue](https://mohammadmd1383.github.io/jdx/docs/reference/) lists all of them. ## Design principles These are product guarantees, not aspirations — each is enforced by tests: - **Minimal correct output.** Bounded by default, never a file dump when a member was asked for. Truncation is explicit and says which flag shows the rest. - **Never guesses.** An ambiguous reference exits `2` and lists every candidate as a copy-pasteable reference. Guessing wrong is worse than asking. - **Provenance on every answer.** Which jar, whether the text came from sources, bytecode or a decompiler — and which one — so the agent can judge confidence. - **Deterministic.** Same inputs, byte-identical output. No timestamps, no absolute paths. - **Meaningful exit codes**, so agents branch without parsing prose ([details](https://mohammadmd1383.github.io/jdx/docs/output/#exit-codes)). - **Degrade, don't fail.** No sources jar? It decompiles — and labels the result as reconstructed. - **Read-only.** Bytecode is parsed with ASM; inspected classes are never loaded, so no code from the jars you inspect ever runs. ## Where to go next 1. [Install jdx](https://mohammadmd1383.github.io/jdx/docs/installation/) (one command, JDK 21+). 2. Run the [quickstart](https://mohammadmd1383.github.io/jdx/docs/quickstart/) — five minutes against the JDK and a Maven library. 3. [Connect your AI agent](https://mohammadmd1383.github.io/jdx/docs/ai-agents/) over MCP or the CLI. --- # Installation jdx installs per user with one command, needs only a Java 21+ runtime, and runs on Linux, macOS and Windows. ## Requirements - **JDK 21 or later.** jdx finds it through `JAVA_HOME`, then `java` on `PATH`, then the usual platform locations (`/usr/libexec/java_home` on macOS, the registry and `%ProgramFiles%\Java` on Windows). If none works it prints one line saying what to set. - No root or administrator rights. jdx refuses to install as root. ## Install the latest release ```bash curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash ``` This downloads the latest release from [GitHub Releases](https://github.com/MohammadMD1383/jdx/releases), installs it into `~/.local/share/jdx` and links `~/.local/bin/jdx`. Make sure `~/.local/bin` is on your `PATH`. Installer options (pass them after `bash -s --`): |Option|Effect| |---|---| |`--version v1.2.0`|Install a specific release tag instead of the latest| |`--dir `|Install directory (default `~/.local/share/jdx`)| |`--bin-dir `|Where the `jdx` link goes (default `~/.local/bin`)| |`--force`|Replace an unrelated `jdx` already on `PATH`| |`--tarball `|Install from a downloaded `.tar.gz` or `.zip` (offline installs)| The same settings are available as environment variables: `JDX_VERSION`, `JDX_INSTALL_DIR`, `JDX_BIN_DIR`, `JDX_TARBALL`. ### Windows and macOS Every release ships per-OS bundles — `jdx--.tar.gz` and `.zip` for `linux`, `macos` and `windows`, each with a `.sha256` checksum and a startup-time archive trained on that OS. On Windows, prefer the `-windows.zip` asset and install it with `sh install-release.sh --tarball jdx--windows.zip`. Releases are not code-signed, so Windows SmartScreen and macOS Gatekeeper warn on first run. Clear the warning with `Unblock-File jdx.bat` (PowerShell) or `xattr -d com.apple.quarantine /jdx` (macOS). ## Verify ```bash jdx version jdx doctor ``` `jdx doctor` prints one `ok` / `warn` / `fail` row per check: JDK, `javap`, cache, config, index, Kotlin module, startup archive, daemon, workspace and agent setup. It exits `6` when a check fails, so scripts can gate on it. ## Upgrade Release installs update themselves: ```bash jdx upgrade --check # is there a newer release? jdx upgrade # install it jdx upgrade --version v1.2.0 ``` See the [changelog](https://mohammadmd1383.github.io/jdx/changelog/) for what changed. ## Build from source ```bash git clone https://github.com/MohammadMD1383/jdx && cd jdx ./gradlew :app:installDist && ./install.sh ``` The Gradle wrapper is the only build entry point — no system Gradle or Maven is needed. See [`CONTRIBUTING.md`](https://mohammadmd1383.github.io/jdx/docs/contributing/) for the development loop. ## Uninstall Optionally wipe the regenerable index cache first, then delete the install directory and the link (defaults shown): ```bash jdx cache clear rm -rf ~/.local/share/jdx ~/.local/bin/jdx ``` ## Next Run the [quickstart](https://mohammadmd1383.github.io/jdx/docs/quickstart/), then [connect your AI agent](https://mohammadmd1383.github.io/jdx/docs/ai-agents/). --- # Quickstart Five minutes, two classpaths: the JDK (no setup at all) and a library pulled by its Maven coordinate. Every output below is real — it was produced by running the command when this page was built. ## 1. Ask about the JDK — zero setup The running JDK is always on the classpath, so this works straight after installing: ```console jdx-generated $ jdx members java.util.HashMap --limit 5 members of java.util.HashMap constructors declared on java.util.HashMap: constructor public HashMap() constructor public HashMap(int initialCapacity) constructor public HashMap(int initialCapacity, float loadFactor) constructor public HashMap(java.util.Map m) methods declared on java.util.HashMap: method public V compute(K key, java.util.function.BiFunction remappingFunction) + 7 from java.lang.Object (--from java.lang.Object to expand) 5 of 53 members shown (--limit 53 to see more) source: java.base (jrt) ``` The footer says what was cut and which `--limit` shows the rest. The `source:` line says where the answer came from. Now read one method — only that method: ```console jdx-generated $ jdx body 'java.util.HashMap#get(java.lang.Object)' java.util.HashMap#get(java.lang.Object) source: src.zip · java.base/java/util/HashMap.java:562-565 public V get(Object key) { Node e; return (e = getNode(key)) == null ? null : e.value; } next: jdx show java.util.HashMap ``` Always quote references in a shell: `#`, `(` and `$` are shell syntax. When there are no sources, jdx decompiles and marks the output `⚠ reconstructed`. ## 2. Ask about a library by Maven coordinate `--coord` resolves a library from your local Gradle and Maven caches; `--fetch` allows downloading it (and its `-sources.jar`) from Maven Central once: ```console jdx-generated $ jdx show com.google.gson.Gson --coord com.google.code.gson:gson:2.14.0 --fetch class com.google.gson.Gson public final class Gson extends java.lang.Object members: 2 constructors, 33 methods, 26 fields warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) source: gson-2.14.0.jar (bytecode) next: jdx members com.google.gson.Gson --inherited ``` With sources available, `body` shows the real source with its file and line range: ```console jdx-generated $ jdx body 'com.google.gson.Gson#toJson(Object)' --coord com.google.code.gson:gson:2.14.0 com.google.gson.Gson#toJson(java.lang.Object) source: gson-2.14.0-sources.jar · com/google/gson/Gson.java:542-547 public String toJson(Object src) { if (src == null) { return toJson(JsonNull.INSTANCE); } return toJson(src, src.getClass()); } warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) next: jdx show com.google.gson.Gson ``` ## 3. Let it refuse to guess `toJson` has several overloads. jdx will not pick one for you — it exits `2` and prints every candidate as a ready-to-paste reference: ```console jdx-generated $ jdx body 'com.google.gson.Gson#toJson' --coord com.google.code.gson:gson:2.14.0 ambiguous: 8 candidates for com.google.gson.Gson#toJson com.google.gson.Gson#toJson(com.google.gson.JsonElement) com.google.gson.Gson#toJson(com.google.gson.JsonElement, com.google.gson.stream.JsonWriter) com.google.gson.Gson#toJson(com.google.gson.JsonElement, java.lang.Appendable) com.google.gson.Gson#toJson(java.lang.Object) com.google.gson.Gson#toJson(java.lang.Object, java.lang.Appendable) com.google.gson.Gson#toJson(java.lang.Object, java.lang.reflect.Type) com.google.gson.Gson#toJson(java.lang.Object, java.lang.reflect.Type, com.google.gson.stream.JsonWriter) com.google.gson.Gson#toJson(java.lang.Object, java.lang.reflect.Type, java.lang.Appendable) hint: re-run with one of the refs above [exit 2] ``` ## 4. Find things and follow the graph ```console jdx-generated $ jdx search '*Json*Reader*' --coord com.google.code.gson:gson:2.14.0 search results for '*Json*Reader*' class com.google.gson.internal.JsonReaderInternalAccess gson-2.14.0.jar class com.google.gson.internal.bind.JsonTreeReader gson-2.14.0.jar class com.google.gson.internal.bind.JsonTreeReader$1 gson-2.14.0.jar class com.google.gson.internal.bind.JsonTreeReader$2 gson-2.14.0.jar class com.google.gson.stream.JsonReader gson-2.14.0.jar class com.google.gson.stream.JsonReader$1 gson-2.14.0.jar warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) $ jdx hierarchy com.google.gson.JsonElement --coord com.google.code.gson:gson:2.14.0 hierarchy of 'com.google.gson.JsonElement' supertypes extends java.lang.Object java.base subtypes in workspace (4) class com.google.gson.JsonArray gson-2.14.0.jar class com.google.gson.JsonNull gson-2.14.0.jar class com.google.gson.JsonObject gson-2.14.0.jar class com.google.gson.JsonPrimitive gson-2.14.0.jar warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` ## 5. Name your classpath once Typing `--coord` on every command gets old. A named workspace stores the classpath; every command (and the MCP server, daemon and HTTP API) can reuse it: ```bash jdx ws create gson --coord com.google.code.gson:gson:2.14.0 jdx -w gson usages com.google.gson.TypeAdapter jdx ws use gson # make it the default for this machine ``` Inside a Gradle or Maven project you often need nothing at all: jdx discovers the project's sources and dependency jars from the working directory. See [Classpath \& workspaces](https://mohammadmd1383.github.io/jdx/docs/classpath/). ## 6. Hand it to your agent ```bash jdx setup --agent claude-code --scope project # or cursor, codex, opencode, copilot, antigravity, … ``` That is the whole integration — see [Use with AI agents](https://mohammadmd1383.github.io/jdx/docs/ai-agents/). ## Where next - [Symbol references](https://mohammadmd1383.github.io/jdx/docs/symbol-references/) — every way to name a class or member. - [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/) and [Usages, hierarchy \& calls](https://mohammadmd1383.github.io/jdx/docs/navigating-code/). - The [command catalogue](https://mohammadmd1383.github.io/jdx/docs/reference/). --- # Use with AI agents Wire jdx into Claude Code, Cursor, Codex, GitHub Copilot, Antigravity, OpenCode, Kilo Code, Cline or any MCP client with one command — or let any shell-capable agent call the CLI directly. ## Option 1: MCP, wired by `jdx setup` (recommended) `jdx mcp` is a Model Context Protocol server over stdio. It exposes every query as a typed tool, so the agent never fights shell quoting of `#` and `(`, and it keeps the index hot in memory for the whole session — repeated questions are instant. `jdx setup` writes the right config entry for your agent: ```bash jdx setup --agent claude-code --scope project # .mcp.json in this checkout jdx setup --agent cursor --scope system # ~/.cursor/mcp.json for every project ``` Then restart the agent. `--scope project` writes into the current checkout (commit it to share with your team); `--scope system` writes the user-global config. Setup merges into existing config and never clobbers unrelated entries; re-running is a no-op. Check or undo it at any time: ```console jdx-generated $ jdx setup --agent claude-code --scope project --check claude-code project setup not installed ./.mcp.json detected: claude-code not found on PATH next: jdx setup --agent claude-code --scope project [exit 1] ``` `--check` exits `1` when jdx is not wired yet (as above) and `0` when it is; `--remove` uninstalls cleanly. Per-agent instructions and the exact files written are on the [integrations pages](https://mohammadmd1383.github.io/jdx/integrations/). ## Option 2: MCP, configured by hand Any MCP client that launches stdio servers works with this entry: ```json {"mcpServers": {"jdx": {"command": "jdx", "args": ["mcp"]}}} ``` Add `"args": ["mcp", "-w", "myproject"]` to serve a named workspace. The tool list, with every parameter, is on the [MCP tools](https://mohammadmd1383.github.io/jdx/docs/reference/mcp-tools/) page. ## Option 3: the CLI plus a cheat sheet Every agent that can run shell commands can use jdx directly. Paste the output of `jdx help --agent` into `CLAUDE.md`, `AGENTS.md`, `.cursorrules` or a system prompt: ```console jdx-generated $ jdx help --agent jdx agent cheat sheet (paste into CLAUDE.md / system prompt): jdx show — Go to declaration jdx members --inherited — Code completion after `.` jdx outline — File structure jdx signature — Parameter info jdx doc — Quick documentation jdx body — Open the method jdx source — Open the file / decompile … 27 more lines (run it yourself to see them all) ``` The full block is on the [agent cheat sheet](https://mohammadmd1383.github.io/jdx/docs/reference/cheat-sheet/) page. ## Tips that make agents better at it - **Teach the exit codes.** `1` means *not found* (read the `did you mean` list), `2` means *ambiguous* (retry with one of the printed candidates), `4` means no classpath resolved. See [exit codes](https://mohammadmd1383.github.io/jdx/docs/output/#exit-codes). - **Prefer `body` over `source`.** A method body is tens of lines; a source file is thousands. - **Name the classpath once** with a [workspace](https://mohammadmd1383.github.io/jdx/docs/classpath/#named-workspaces) and start the MCP server or daemon on it. - **Use `--json`** when the agent post-processes output; it carries exactly the same information as text. - **Follow the `next:` line.** Single-entity answers end with the most useful follow-up command. --- # Symbol references Every jdx command takes a symbol reference in Javadoc style. jdx is generous about what it accepts and canonical about what it prints, so any reference it prints can be pasted back into the next command. ## The forms |Reference|Means| |---|---| |`com.google.gson.Gson`|a type| |`java.util.Map$Entry`, `java.util.Map.Entry`|a nested type (`$` or `.`)| |`Gson`|a short name — resolved, or exit `2` with candidates| |`com.google.gson.Gson#toJson`|every overload of a method| |`com.google.gson.Gson#toJson(Object)`|one overload, simple parameter names| |`com.google.gson.Gson#toJson(java.lang.Object)`|one overload, qualified parameter names| |`…#toJson(Object):String`|disambiguated by return type (bridge methods)| |`…#toJson(Ljava/lang/Object;)Ljava/lang/String;`|an exact JVM descriptor — never ambiguous| |`com.google.gson.Gson#(...)`|a constructor| |`java.lang.Integer#MAX_VALUE`|a field| |`com.google.code.gson:gson:2.14.0/com.google.gson.Gson`|scoped to one Maven artifact| `::` and `.` are accepted in place of `#`. The last dot-separated segment before the member is always the class. **Quote references in a shell.** `#` starts a comment, `(` opens a subshell and `$` expands a variable — single quotes avoid all three. ## Nested types Both spellings resolve to the same type, and jdx prints the canonical `$` form: ```console jdx-generated $ jdx show java.util.Map.Entry interface java.util.Map$Entry public abstract interface Entry members: 0 constructors, 15 methods, 0 fields source: java.base (jrt) next: jdx members java.util.Map$Entry --inherited $ jdx show 'java.util.Map$Entry' interface java.util.Map$Entry public abstract interface Entry members: 0 constructors, 15 methods, 0 fields source: java.base (jrt) next: jdx members java.util.Map$Entry --inherited ``` ## Overloads and descriptors Simple names, qualified names and raw descriptors all select a single overload; fields work the same way: ```console jdx-generated $ jdx signature 'java.util.HashMap#put(Object, Object)' signatures of java.util.HashMap#put public V put(K key, V value) source: java.base (jrt) next: jdx show java.util.HashMap $ jdx signature 'java.util.HashMap#get(Ljava/lang/Object;)Ljava/lang/Object;' signatures of java.util.HashMap#get public V get(java.lang.Object key) source: java.base (jrt) next: jdx show java.util.HashMap $ jdx signature 'java.lang.Integer#MAX_VALUE' signatures of java.lang.Integer#MAX_VALUE public static final int MAX_VALUE = 2147483647 source: java.base (jrt) next: jdx show java.lang.Integer ``` ## Ambiguity: jdx never guesses When a reference matches more than one symbol, jdx exits `2` and prints every candidate in its canonical form. An agent retries with one of them — no reconstruction needed: ```console jdx-generated $ jdx signature 'com.google.gson.Gson#toJson(Object, Appendable)' --coord com.google.code.gson:gson:2.14.0 signatures of com.google.gson.Gson#toJson public void toJson(java.lang.Object src, java.lang.Appendable writer) throws com.google.gson.JsonIOException warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) source: gson-2.14.0.jar (bytecode) next: jdx show com.google.gson.Gson $ jdx body 'com.google.gson.Gson#toJson' --coord com.google.code.gson:gson:2.14.0 ambiguous: 8 candidates for com.google.gson.Gson#toJson com.google.gson.Gson#toJson(com.google.gson.JsonElement) com.google.gson.Gson#toJson(com.google.gson.JsonElement, com.google.gson.stream.JsonWriter) com.google.gson.Gson#toJson(com.google.gson.JsonElement, java.lang.Appendable) com.google.gson.Gson#toJson(java.lang.Object) com.google.gson.Gson#toJson(java.lang.Object, java.lang.Appendable) com.google.gson.Gson#toJson(java.lang.Object, java.lang.reflect.Type) com.google.gson.Gson#toJson(java.lang.Object, java.lang.reflect.Type, com.google.gson.stream.JsonWriter) com.google.gson.Gson#toJson(java.lang.Object, java.lang.reflect.Type, java.lang.Appendable) hint: re-run with one of the refs above [exit 2] ``` ## Not found: did you mean A miss exits `1` with the closest candidates (camel humps, then edit distance, then the same simple name in other packages): ```console jdx-generated $ jdx show com.google.gson.JsonParse --coord com.google.code.gson:gson:2.14.0 not found: com.google.gson.JsonParse did you mean: com.google.gson.JsonParser [exit 1] ``` When you only know a simple name — from a stack trace or a snippet — `jdx resolve` lists every candidate with its kind and artifact, and several candidates are a success: ```console jdx-generated $ jdx resolve JsonParser --coord com.google.code.gson:gson:2.14.0 search results for 'JsonParser' class com.google.gson.JsonParser gson-2.14.0.jar warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` ## Round-tripping `parse(print(ref)) == ref` is a tested property: every reference jdx prints — in `members`, `usages`, `callers`, ambiguity lists, `next:` hints — is accepted verbatim as input. The full grammar is in the [design specification](https://mohammadmd1383.github.io/jdx/docs/design/#6-symbol-reference-grammar). --- # Classpath & workspaces jdx answers from a classpath made of jars, class directories, source directories, Maven coordinates and the JDK. This guide covers every way to supply one — per command, as a named workspace, or by letting jdx discover your project. ## The JDK is always there The running JDK's standard library is on every classpath (through `jrt:/`), paired with its `src.zip` when the JDK ships one. Pass `--no-jdk` to leave it out. ## Per-command flags |Flag|Adds| |---|---| |`--jars `|jar files, class directories or globs (repeatable)| |`--coord g:a:v`|a Maven artifact, plus its `-sources.jar` when available (repeatable)| |`--fetch`|permission to download `--coord` artifacts; without it, only local caches are used| |`--repo `|an extra Maven repository, tried before Maven Central (repeatable)| |`--no-jdk`|removes the JDK| `jdx usages` additionally takes `--src ` for project source directories. ```console jdx-generated $ jdx ls com.google.gson.stream --coord com.google.code.gson:gson:2.14.0 types in package 'com.google.gson.stream' package com.google.gson.stream (7 types) class com.google.gson.stream.JsonReader gson-2.14.0.jar class com.google.gson.stream.JsonReader$1 gson-2.14.0.jar class com.google.gson.stream.JsonScope gson-2.14.0.jar enum com.google.gson.stream.JsonToken gson-2.14.0.jar class com.google.gson.stream.JsonWriter gson-2.14.0.jar class com.google.gson.stream.MalformedJsonException gson-2.14.0.jar … 2 more lines (run it yourself to see them all) ``` ### Maven coordinates Coordinates resolve from `~/.gradle/caches` and `~/.m2` first — usually no download is needed. With `--fetch`, a missing artifact is downloaded from Maven Central (or your `--repo` mirrors) into the jdx cache, with checksum verification. Network access is opt-in per invocation: jdx never downloads anything you did not ask for. ## Named workspaces A workspace is a named, ordered classpath stored once and reused everywhere — the CLI, the MCP server, the daemon, the HTTP API and `jdx batch`: ```bash jdx ws create myapp --jars 'libs/*.jar' --src ./src --coord com.google.code.gson:gson:2.14.0 jdx ws add myapp build/classes/java/main jdx ws list jdx ws info myapp ``` Select one per command with `-w`, per shell with `JDX_WORKSPACE`, or per machine with `jdx ws use`: ```bash jdx -w myapp usages 'com.example.Service#run()' export JDX_WORKSPACE=myapp jdx ws use myapp # default when neither -w nor JDX_WORKSPACE is set jdx ws use --clear ``` Explicit `--jars` / `--coord` flags always merge in front of the selected workspace. ## Project auto-discovery With no workspace selected, jdx walks up from the working directory looking for `settings.gradle(.kts)`, `build.gradle(.kts)`, `pom.xml` or `.idea/`. On a hit it assembles a classpath from the project's own sources and build output plus the dependency jars it can find in the Gradle and Maven caches. jdx never runs Gradle or Maven to do this; when it cannot pin down the exact dependency set it says so with a `PROJECT_DISCOVERY_FALLBACK` warning. ## Resolution order 1. Explicit flags (`--jars`, `--src`, `--coord`). 2. `-w `. 3. `JDX_WORKSPACE`, then the `jdx ws use` default. 4. Project auto-discovery. 5. The JDK — always appended unless `--no-jdk`. When nothing resolves for a command that needs a classpath, jdx exits `4`. ## The index cache jdx keeps a persistent index keyed by content hash, so an unchanged jar is never re-read. Everything in it can be regenerated: ```bash jdx cache info # location, size, artifact and class counts jdx cache gc # evict artifacts no workspace references jdx cache clear # wipe it; re-indexed on demand ``` Cache, config and runtime directories follow each OS's conventions (`~/.cache/jdx` and `$XDG_*` on Linux, `~/Library/Caches` on macOS, `%LOCALAPPDATA%` on Windows) and can be overridden with `JDX_*` variables — the full table is in the [design specification](https://mohammadmd1383.github.io/jdx/docs/design/#17-security-and-safety). --- # Reading code Look at a type, list what you can call on it, read one method, or search the whole classpath — each with one command that returns only what was asked. This guide covers `show`, `members`, `outline`, `signature`, `doc`, `body`, `source`, `search`, `resolve`, `ls`, `tree`, and Kotlin. The examples use Gson by Maven coordinate; any classpath works the same way ([Classpath \& workspaces](https://mohammadmd1383.github.io/jdx/docs/classpath/)). ## A type at a glance: `show` ```console jdx-generated $ jdx show com.google.gson.JsonArray --coord com.google.code.gson:gson:2.14.0 class com.google.gson.JsonArray public final class JsonArray extends com.google.gson.JsonElement implements java.lang.Iterable members: 2 constructors, 32 methods, 1 field warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) source: gson-2.14.0.jar (bytecode) next: jdx members com.google.gson.JsonArray --inherited ``` ## What can I call? `members` and `outline` `members` is the `.` completion of an IDE: inherited members are included and grouped by the type that declares them, with `java.lang.Object` collapsed to one line. ```console jdx-generated $ jdx members com.google.gson.JsonArray --kind method --limit 10 --coord com.google.code.gson:gson:2.14.0 members of com.google.gson.JsonArray methods declared on com.google.gson.JsonArray: method public boolean contains(com.google.gson.JsonElement element) method public boolean equals(java.lang.Object o) method public boolean getAsBoolean() method public boolean isEmpty() method public boolean remove(com.google.gson.JsonElement element) method public byte getAsByte() method public char getAsCharacter() method public com.google.gson.JsonArray deepCopy() method public com.google.gson.JsonElement get(int i) method public com.google.gson.JsonElement remove(int index) + 8 from java.lang.Object (--from java.lang.Object to expand) 10 of 50 members shown (--limit 50 to see more) warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) source: gson-2.14.0.jar (bytecode) ``` Narrow it down instead of reading more: |Want|Flag| |---|---| |only this type's own members|`--declared` (or use `jdx outline`)| |methods / fields / constructors|`--kind method\|field\|ctor\|property`| |visibility|`--access public\|protected\|package\|private\|all`| |statics or instances|`--static` / `--instance`| |members from one supertype|`--from java.util.AbstractList`| |names matching a regex|`--grep '^get'`| |the first javadoc sentence per member|`--with-doc`| |a hard token budget|`--brief`, `--max-lines N`| ```console jdx-generated $ jdx members com.google.gson.JsonArray --grep '^as' --declared --coord com.google.code.gson:gson:2.14.0 members of com.google.gson.JsonArray methods declared on com.google.gson.JsonArray: method public java.util.List asList() warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) source: gson-2.14.0.jar (bytecode) ``` `outline` prints one dense line per declared member — the file-structure view. ## One member: `signature`, `doc`, `body` `signature` shows every overload with real parameter names, generics, `throws` and defaults: ```console jdx-generated $ jdx signature 'com.google.gson.JsonParser#parseString' --coord com.google.code.gson:gson:2.14.0 signatures of com.google.gson.JsonParser#parseString public static com.google.gson.JsonElement parseString(java.lang.String json) throws com.google.gson.JsonSyntaxException warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) source: gson-2.14.0.jar (bytecode) next: jdx show com.google.gson.JsonParser ``` `doc` renders the javadoc as plain text; an undocumented override falls back to the nearest documented supertype (and says so): ```console jdx-generated $ jdx doc 'com.google.gson.JsonParser#parseString(String)' --coord com.google.code.gson:gson:2.14.0 com.google.gson.JsonParser#parseString(java.lang.String) source: gson-2.14.0-sources.jar · com/google/gson/JsonParser.java:80-90 Parses the specified JSON string into a parse tree. An exception is thrown if the JSON string has multiple top-level JSON elements, or if there is trailing data. The JSON string is parsed in lenient mode. @param json JSON text @return a parse tree of JsonElements corresponding to the specified JSON @throws JsonParseException if the specified text is not valid JSON @since 2.8.6 warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) … 1 more lines (run it yourself to see them all) ``` `body` shows exactly one method — tens of lines instead of a whole file: ```console jdx-generated $ jdx body 'com.google.gson.JsonParser#parseString(String)' --coord com.google.code.gson:gson:2.14.0 com.google.gson.JsonParser#parseString(java.lang.String) source: gson-2.14.0-sources.jar · com/google/gson/JsonParser.java:91-93 public static JsonElement parseString(String json) throws JsonSyntaxException { return parseReader(new StringReader(json)); } warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) next: jdx show com.google.gson.JsonParser ``` Useful flags: `--line-numbers`, `--context N` (lines around the body), `--with-doc`, `--with-signature`, `--max-lines N`. ## A whole file, sliced: `source` When you do need more than one member, slice the file instead of dumping it: ```bash jdx source com.google.gson.Gson --lines 120:160 jdx source com.google.gson.Gson --around 'com.google.gson.Gson#toJson(Object)' --context 5 ``` ## Sources, decompilation, bytecode jdx always prefers real sources (a paired `-sources.jar`, a source directory, the JDK's `src.zip`). Without them it decompiles with Vineflower and marks the result `⚠ reconstructed`; `--engine javap` shows raw bytecode instead: ```console jdx-generated $ jdx body 'java.util.HashMap#get(java.lang.Object)' --engine javap java.util.HashMap#get(java.lang.Object) source: disassembled by javap from java.base · java/util/HashMap.java:346-359 ⚠ reconstructed public V get(java.lang.Object); descriptor: (Ljava/lang/Object;)Ljava/lang/Object; Code: 0: aload_0 1: aload_1 2: invokevirtual #158 // Method getNode:(Ljava/lang/Object;)Ljava/util/HashMap$Node; 5: dup 6: astore_2 7: ifnonnull 14 10: aconst_null … 5 more lines (run it yourself to see them all) ``` When sources and binary disagree, jdx warns `SOURCES_VERSION_MISMATCH` — bytecode is the truth for structure, sources supply bodies, names and docs. ## Search the classpath: `search`, `resolve`, `ls`, `tree` ```console jdx-generated $ jdx search 'JsonParser*' --coord com.google.code.gson:gson:2.14.0 search results for 'JsonParser*' class com.google.gson.JsonParser gson-2.14.0.jar warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) $ jdx search 'parse*' --kind method --in 'gson*' --coord com.google.code.gson:gson:2.14.0 search results for 'parse*' method com.google.gson.JsonParser#parse(com.google.gson.stream.JsonReader) gson-2.14.0.jar method com.google.gson.JsonParser#parse(java.io.Reader) gson-2.14.0.jar method com.google.gson.JsonParser#parse(java.lang.String) gson-2.14.0.jar method com.google.gson.JsonParser#parseReader(com.google.gson.stream.JsonReader) gson-2.14.0.jar method com.google.gson.JsonParser#parseReader(java.io.Reader) gson-2.14.0.jar method com.google.gson.JsonParser#parseString(java.lang.String) gson-2.14.0.jar method com.google.gson.ToNumberPolicy$3#parseAsDouble(java.lang.String, com.google.gson.stream.JsonReader) gson-2.14.0.jar method com.google.gson.internal.JavaVersion#parseDotted(java.lang.String) gson-2.14.0.jar method com.google.gson.internal.JavaVersion#parseMajorJavaVersion(java.lang.String) gson-2.14.0.jar … 6 more lines (run it yourself to see them all) ``` Globs by default, `--regex` for regular expressions; bare words match case-insensitively or as camel humps (`HMap` finds `HashMap`); `--fuzzy` retries a miss by edit distance. `--kind` narrows to classes, interfaces, methods, fields, packages or modules. - `jdx resolve ` — every candidate for a simple name, with kind and artifact. - `jdx ls 'com.google.*'` — packages with type counts; an exact package lists its types. - `jdx tree 'gson*'` — the package tree of an artifact (`--depth`, `--counts`). ## Kotlin Kotlin binaries are read through their `@Metadata`, so jdx shows true Kotlin declarations — `suspend` functions, properties, default arguments, `@JvmName` mappings — rather than the JVM projection. `--view jvm` shows the raw JVM view (getters, `Continuation` parameters, mangled names) when that is what you need. Reading Kotlin *sources* (`body`, `source`, `doc` on `.kt` files) uses the Kotlin compiler's parser, which is installed separately on demand to keep jdx small: ```bash jdx kotlin install ``` Without it, Kotlin binaries still render from metadata and source queries degrade with a label. --- # Usages, hierarchy & calls Find who uses a symbol, what implements a type, who calls a method and what it calls, and real call sites to learn from — across every jar on the classpath, not just one. ## Find usages: `usages` One row per referencing method, named canonically so the next command is a copy-paste: ```console jdx-generated $ jdx usages com.google.gson.JsonNull --limit 8 --coord com.google.code.gson:gson:2.14.0 usages of 'com.google.gson.JsonNull' gson-2.14.0.jar (8) read com.google.gson.Gson#toJson(java.lang.Object) -> #INSTANCE read com.google.gson.Gson#toJson(java.lang.Object, java.lang.Appendable) -> #INSTANCE read com.google.gson.Gson#toJsonTree(java.lang.Object) -> #INSTANCE read com.google.gson.JsonArray#add(com.google.gson.JsonElement) -> #INSTANCE read com.google.gson.JsonArray#add(java.lang.Boolean) -> #INSTANCE read com.google.gson.JsonArray#add(java.lang.Character) -> #INSTANCE read com.google.gson.JsonArray#add(java.lang.Number) -> #INSTANCE read com.google.gson.JsonArray#add(java.lang.String) -> #INSTANCE 8 of 30 usages shown (--limit 30 to see more) warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` Narrow by edge kind with `--kind`: |`--kind`|Finds| |---|---| |`call`|method calls| |`read` / `write`|field reads and writes| |`new`|constructor call sites| |`ref`|any other reference (casts, `instanceof`, class literals, signatures)| |`throw`|methods declaring the type in `throws` (declaration-level, not `ATHROW` throw sites)| |`annotation`|classes and members carrying the annotation| `--in` / `--exclude` filter by artifact (jar file name, JDK module or source directory), and `--src ` adds your project's source directories — those hits are textual mentions with line numbers. ## Type hierarchy: `hierarchy` and `implementors` Supertypes upward (the full chain, interfaces included) and subtypes downward across the whole classpath: ```console jdx-generated $ jdx hierarchy com.google.gson.JsonElement --coord com.google.code.gson:gson:2.14.0 hierarchy of 'com.google.gson.JsonElement' supertypes extends java.lang.Object java.base subtypes in workspace (4) class com.google.gson.JsonArray gson-2.14.0.jar class com.google.gson.JsonNull gson-2.14.0.jar class com.google.gson.JsonObject gson-2.14.0.jar class com.google.gson.JsonPrimitive gson-2.14.0.jar warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` `--up` / `--down` pick a direction, `--direct` stops at one level, `--depth N` caps it. `jdx implementors ` is `hierarchy --down`: ```console jdx-generated $ jdx implementors com.google.gson.TypeAdapterFactory --direct --coord com.google.code.gson:gson:2.14.0 hierarchy of 'com.google.gson.TypeAdapterFactory' subtypes in workspace (21) class com.google.gson.internal.Excluder gson-2.14.0.jar class com.google.gson.internal.bind.ArrayTypeAdapter$1 gson-2.14.0.jar class com.google.gson.internal.bind.CollectionTypeAdapterFactory gson-2.14.0.jar class com.google.gson.internal.bind.DefaultDateTypeAdapter$1 gson-2.14.0.jar class com.google.gson.internal.bind.EnumTypeAdapter$1 gson-2.14.0.jar class com.google.gson.internal.bind.JavaTimeTypeAdapters$14 gson-2.14.0.jar class com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory gson-2.14.0.jar class com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory$DummyTypeAdapterFactory gson-2.14.0.jar … 14 more lines (run it yourself to see them all) ``` ## Call hierarchy: `callers` and `calls` ```console jdx-generated $ jdx callers 'com.google.gson.JsonParser#parseReader(com.google.gson.stream.JsonReader)' --coord com.google.code.gson:gson:2.14.0 callers of 'com.google.gson.JsonParser#parseReader(com.google.gson.stream.JsonReader)' call com.google.gson.JsonParser#parse(com.google.gson.stream.JsonReader) gson-2.14.0.jar call com.google.gson.JsonParser#parseReader(java.io.Reader) gson-2.14.0.jar warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` `--depth N` follows the graph transitively (cycle-safe, cycles are marked). `jdx calls` goes the other way; add `--external-only` to see only what a method needs from *other* artifacts — its dependency footprint. ## Learn from real code: `samples` The fastest way to learn an API is to read code that already uses it. `samples` ranks real call sites by how exemplary they are — non-test before test, non-generated before generated, fuller overloads first — and shows the enclosing method when sources are available: ```console jdx-generated $ jdx samples 'com.google.gson.JsonParser#parseString(String)' --coord com.google.code.gson:gson:2.14.0 samples of 'com.google.gson.JsonParser#parseString(String)' example com.google.gson.JsonParser#parse(java.lang.String) gson-2.14.0.jar com/google/gson/JsonParser.java:155-159 @Deprecated @InlineMe(replacement = "JsonParser.parseString(json)", imports = "com.google.gson.JsonParser") public JsonElement parse(String json) throws JsonSyntaxException { return parseString(json); } warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` ## Limitations - Graph queries re-scan the bytecode roots on each query (there is no persistent graph index yet); the daemon and MCP server keep the scan warm. - Call matching is exact name + descriptor, not override-aware: a call through an interface is attributed to the interface method. - Usages found in source directories are textual (`ref` kind only). - `usages --kind throw` reads `throws` declarations, not `ATHROW` sites (throw-site attribution needs data-flow analysis and is deferred to future work). Open limitations are tracked in [GitHub Issues](https://github.com/MohammadMD1383/jdx/issues). --- # API diff for upgrades Before bumping a dependency, ask what the new version breaks. `jdx diff` compares the public API of two artifacts — jars, class directories or Maven coordinates — and names every difference as a rule with a severity, with a gate for CI. ## Compare two versions ```console jdx-generated $ jdx diff com.google.code.gson:gson:2.11.0 com.google.code.gson:gson:2.14.0 --fetch api diff com.google.code.gson:gson:2.11.0 -> com.google.code.gson:gson:2.14.0 (public surface) info TYPE_ADDED com.google.gson.internal.GsonTypes info TYPE_ADDED com.google.gson.internal.bind.JsonElementTypeAdapter info MEMBER_ADDED com.google.gson.FieldNamingStrategy#alternateNames(java.lang.reflect.Field) info MEMBER_ADDED com.google.gson.internal.ConstructorConstructor#get(com.google.gson.reflect.TypeToken, boolean) info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#BIG_DECIMAL_FACTORY info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#BIG_INTEGER_FACTORY info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#DOUBLE_STRICT info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#FLOAT_STRICT info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#LAZILY_PARSED_NUMBER_FACTORY info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#LONG_AS_STRING info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#atomicLongAdapter(com.google.gson.TypeAdapter) info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#atomicLongArrayAdapter(com.google.gson.TypeAdapter) info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#javaTimeTypeAdapterFactory() info MEMBER_ADDED com.google.gson.internal.sql.SqlTypesSupport#SQL_TYPE_FACTORIES info MEMBER_ADDED com.google.gson.stream.JsonReader#getNestingLimit() info MEMBER_ADDED com.google.gson.stream.JsonReader#setNestingLimit(int) info FIELD_CONSTANT_VALUE_CHANGED com.google.gson.internal.GsonBuildConfig#VERSION: "2.11.0" -> "2.14.0" 0 breaking · 0 suspicious · 17 informational · 2 types added · 0 types removed warning MULTI_RELEASE_VARIANT: in com.google.code.gson:gson:2.11.0: multi-release gson-2.11.0.jar serves version-specific variants for this JDK (21) … 9 more lines (run it yourself to see them all) ``` Each side can be a jar file, a class directory, a glob or a `group:artifact:version` coordinate (with `--fetch` to download it). `diff` reads bytecode only — no sources, no decompilation, no workspace. ## Rules and severities Every finding names exactly one rule, so tools can filter on the rule rather than the prose: - **breaking** — an already-compiled caller can fail to link or run: `MEMBER_REMOVED`, `RETURN_TYPE_CHANGED`, `PARAMETER_TYPE_CHANGED`, `MEMBER_VISIBILITY_NARROWED`, `STATIC_TO_INSTANCE`, `INTERFACE_METHOD_ADDED`, `TYPE_REMOVED`, … - **suspicious** — binary-compatible by the letter of the JLS, but recompiling, the Kotlin view or behaviour can still break: `CHECKED_EXCEPTION_ADDED`, `GENERIC_SIGNATURE_CHANGED`, `MEMBER_MOVED_TO_SUPERTYPE`, `KOTLIN_NULLABILITY_CHANGED`, `KOTLIN_SUSPEND_CHANGED`, … - **info** — additive or cosmetic: `MEMBER_ADDED`, `TYPE_ADDED`, `DEPRECATED_ADDED`, `FIELD_CONSTANT_VALUE_CHANGED`, … A removed member paired with a matching added one is reported once as a retyped signature, not as a removal plus an addition — and an ambiguous pairing is never guessed. The full rule set is in the [design specification](https://mohammadmd1383.github.io/jdx/docs/design/#77-public-api-diff). Useful flags: |Flag|Effect| |---|---| |`--severity breaking\|suspicious\|all`|how much to print (the tally always covers everything)| |`--visibility public\|all`|public + protected surface (default), or every declared member| |`--include-synthetic`|also compare bridge and synthetic members| |`--limit N`|cap the findings shown (default 200; the cut tail is the least severe)| ## Gate a pull request `--fail-on breaking` (or `--fail-on any`) turns the report into an exit status: `1` when the gate trips, `0` otherwise. This is the one documented exception to the [exit-code table](https://mohammadmd1383.github.io/jdx/docs/output/#exit-codes): `diff` has no "not found" outcome, so `1` means "the gate tripped". The verdict is also in the JSON envelope as `result.gate`. ```console jdx-generated $ jdx diff com.google.code.gson:gson:2.11.0 com.google.code.gson:gson:2.14.0 --severity breaking --fail-on breaking api diff com.google.code.gson:gson:2.11.0 -> com.google.code.gson:gson:2.14.0 (public surface) 17 findings below --severity breaking 0 breaking · 0 suspicious · 17 informational · 2 types added · 0 types removed fail-on breaking: no breaking changes (exit 0) warning MULTI_RELEASE_VARIANT: in com.google.code.gson:gson:2.11.0: multi-release gson-2.11.0.jar serves version-specific variants for this JDK (21) warning MULTI_RELEASE_VARIANT: in com.google.code.gson:gson:2.14.0: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) … 7 more lines (run it yourself to see them all) ``` In GitHub Actions: ```yaml - name: Public API must not break run: jdx diff "com.example:mylib:$(git describe --tags --abbrev=0 | tr -d v)" build/libs/mylib.jar --fetch --fail-on breaking ``` --- # Output, JSON & exit codes jdx output is designed to be read by a language model with a finite context window: compact text by default, a complete JSON envelope on request, and exit codes an agent can branch on without parsing prose. ## Text output - **Two-space indentation**, no box drawing or tables — they cost tokens and tokenize badly. - **A leading kind word** on every entity line (`class`, `method`, `field`, `constructor`). - **Grouping headers** instead of repeating the declaring type on every line. - **Explicit truncation**: a footer says how much was shown and which flag shows the rest. - **Provenance**: a `source:` line names the artifact and whether the answer came from sources, bytecode or a decompiler. - **A `next:` line** on single-entity answers with the most useful follow-up command. - **No colour when piped.** ANSI colour only on a terminal (`--no-color` turns it off there too). ```console jdx-generated $ jdx members java.util.List --limit 3 members of java.util.List methods declared on java.util.List: method public E getFirst() method public E getLast() method public E removeFirst() + 9 from java.lang.Object (--from java.lang.Object to expand) 3 of 63 members shown (--limit 63 to see more) source: java.base (jrt) ``` ## Warnings Anything an agent should know but did not ask for is a named warning, never silent: duplicate classes across jars (shading), sources that do not match the binary, corrupt class files, multi-release variants, unresolvable supertypes, project-discovery fallbacks. Each has a stable code (`SOURCES_VERSION_MISMATCH`, `MULTI_RELEASE_VARIANT`, …) in both text and JSON. ## JSON output Add `--json` to any command (before or after the command name). The envelope is the same for the CLI, `jdx batch`, the daemon, the HTTP API and MCP, and carries exactly the information of the text output: ```console jdx-generated $ jdx members java.util.List --limit 2 --json {"jdx":1,"ok":true,"command":"members","query":"java.util.List","result":{"groups":[{"declaring":"java.util.List","depth":0,"kind":"method","rows":[{"ref":"java.util.List#getFirst()","kind":"method","declaring":"java.util.List","signature":"public E getFirst()","depth":0,"overridden":["java.util.SequencedCollection"]},{"ref":"java.util.List#getLast()","kind":"method","declaring":"java.util.List","signature":"public E getLast()","depth":0,"overridden":["java.util.SequencedCollection"]}]}],"counts":{"constructors":0,"methods":63,"fields":0},"objectSummary":{"count":9,"expandHint":"--from java.lang.Object"}},"truncated":{"shown":2,"total":63,"hint":"--limit 63"},"warnings":[],"provenance":[{"artifact":"java.base","origin":"jrt"}]} ``` |Field|Meaning| |---|---| |`jdx`|envelope version (`1`)| |`ok`|`true` on success; `false` carries `error.code` and, when ambiguous, `candidates[]`| |`command`, `query`|what was asked| |`result`|the command-specific answer| |`truncated`|`shown` / `total` / `hint` when output was cut| |`warnings`|`{code, message}` objects| |`provenance`|where each answer came from: artifact, origin, file, lines| ## Exit codes Exit codes are a public contract — they do not change between releases. |Code|Meaning|What an agent should do| |---|---|---| |`0`|success, results found|use the answer| |`1`|valid query, nothing found|read the `did you mean` list or widen the classpath| |`2`|ambiguous reference|retry with one of the printed candidates| |`3`|usage or argument error|fix the command (wrong flag, member ref where a type is required, …)| |`4`|no index or workspace resolved|pass `--jars` / `--coord` / `-w`, or run inside a project| |`5`|artifact read error|the jar is corrupt or unreadable| |`6`|internal error|report a bug| The one documented exception is `jdx diff --fail-on`, which exits `1` when its gate trips ([API diff](https://mohammadmd1383.github.io/jdx/docs/api-diff/#gate-a-pull-request)). ## Bounded by default Listings stop at `--limit` (50 for most commands) and say so. For the biggest listings, `--brief` drops headers and provenance, and `--max-lines N` sets a hard line budget — useful when an agent is short on context. Neither changes `--json` output. ## Deterministic The same inputs give byte-identical output: everything is sorted, and there are no timestamps, absolute paths or hash values in default output. Agents can cache answers, and diffs of jdx output are meaningful. --- # MCP, daemon, HTTP & batch The one-shot CLI starts in about a quarter of a second. When an agent asks dozens of questions, keep jdx warm instead: an MCP server, a background daemon, a local HTTP/JSON API, or a batch of queries in one process. All four are thin adapters over the same engine and speak the same JSON envelope. ## MCP server: `jdx mcp` ```bash jdx mcp # serve the default workspace over stdio jdx mcp -w myapp # serve a named workspace ``` MCP clients start it as a child process (`jdx setup` writes that config for you — see [Use with AI agents](https://mohammadmd1383.github.io/jdx/docs/ai-agents/)). The server holds the index in memory for the session, so repeated queries are instant, and each query is a typed `jdx_*` tool with a JSON schema — the agent never has to quote `#` or `(` for a shell. The tool list is on the [MCP tools](https://mohammadmd1383.github.io/jdx/docs/reference/mcp-tools/) page. ## Daemon: `jdx daemon` A background JVM with a hot index, listening on a local socket. While it runs, ordinary `jdx` commands for that workspace are forwarded to it automatically — per-query latency drops from roughly 250 ms to roughly 20 ms. ```bash jdx daemon start --workspace myapp # idempotent; waits until it answers jdx -w myapp members com.example.Service jdx daemon status --workspace myapp # uptime, memory, indexed artifacts, query count jdx daemon stop --workspace myapp ``` It shuts itself down after 5 minutes without requests (`--idle 30m`, or `--idle 0` to disable). `--no-daemon` on any command forces in-process execution. ## HTTP/JSON API: `jdx serve` For editor plugins, scripts, notebooks and non-MCP agents: ```bash jdx serve -w myapp --port 7070 curl 'http://127.0.0.1:7070/v1/members?query=com.example.Service&limit=20' curl -X POST --data-binary @queries.ndjson http://127.0.0.1:7070/v1/batch curl http://127.0.0.1:7070/v1/health ``` `GET /v1/?query=…&=…` exists for every query command, answers are the exact `--json` envelopes, and the server binds to `127.0.0.1` unless `--bind` says otherwise. ## Batch: `jdx batch` Many queries, one process, one index load. Feed one JSON request per line on stdin; get one envelope per line on stdout: ```console jdx-generated $ echo '{"command":"show","query":"java.util.Map"}' | jdx batch {"jdx":1,"ok":true,"command":"show","query":"java.util.Map","result":{"kind":"interface","declaration":"public abstract interface Map","modifiers":["public","abstract"],"interfaces":[],"counts":{"constructors":0,"methods":39,"fields":0}},"warnings":[],"provenance":[{"artifact":"java.base","origin":"jrt"}]} ``` Request lines look like `{"command":"members","query":"…","params":{"limit":"5"}}`; `params` uses the same names as the CLI flags. A failing query rides in its own envelope and never stops the stream; the exit code is the highest exit code of any query. ## Performance |Path|Target| |---|---| |Cold CLI, simple query|≤ 250 ms| |Warm query (daemon or MCP)|≤ 20 ms| |Re-run on an unchanged workspace|~0 (content-hash short-circuit)| `jdx bench` runs a fixed workload against your classpath and prints the timings next to these targets. --- # jdx command reference The user-facing catalogue. Every shipped flag must appear in `jdx --help`, here, and in `docs/PROPOSAL.md` Appendix B. (Moved out of `README.md` to keep it a hook, demo, install, quickstart, and links.) `jdx --help` documents each command; `jdx help --agent` prints the compact agent block. Every query command accepts `--json` (same information, structured envelope) and `--no-daemon` (force in-process even when the daemon is up). ## Read: declaration, members, structure |Command|IDE equivalent|Key flags| |---|---|---| |`jdx show `|Go to declaration|classpath flags only| |`jdx members `|Code completion after `.`|`--declared` / `--inherited` (default), `--kind all\|method\|field\|ctor\|property`, `--access public\|protected\|package\|private\|all`, `--static` / `--instance`, `--from `, `--grep `, `--include-synthetic`, `--limit` (50), `--with-doc`, `--brief`, `--max-lines`, `--sort kind\|name\|declaring`, `--view kotlin\|jvm`| |`jdx outline `|File structure (`Ctrl+F12`)|Same filters as `members --declared` (never inherited): `--kind`, `--access`, `--static` / `--instance`, `--grep`, `--include-synthetic`, `--limit`, `--with-doc`, `--brief`, `--max-lines`, `--sort`, `--view`| |`jdx signature `|Parameter info (`Ctrl+P`)|`--include-synthetic`, `--view kotlin\|jvm`, `--limit` (50)| |`jdx doc `|Quick documentation (`Ctrl+Q`)|`--inherited` (default) / `--no-inherited`, `--raw`, `--max-lines` (200). Undocumented members fall back to the nearest documenting supertype.| |`jdx body `|Open the method|`--context N`, `--line-numbers`, `--max-lines` (200), `--engine vineflower\|javap`, `--with-doc`, `--with-signature`| |`jdx source `|Open the file / decompile|`--lines A:B`, `--around '#'` + `--context N`, `--line-numbers`, `--max-lines` (200), `--engine vineflower\|javap`| `body`/`source` ladder: paired sources → Vineflower reconstruction → `javap` disassembly → signatures → not found. `--engine vineflower` forces reconstruction even when sources are paired; `--engine javap` shows raw bytecode. Decompiled output is always labelled `⚠ reconstructed` with the engine named. ## Search and browse |Command|IDE equivalent|Key flags| |---|---|---| |`jdx search `|Search everywhere (`Shift Shift`)|`--kind all\|type\|class\|interface\|enum\|record\|annotation\|object\|companion\|method\|field\|package\|module`, `--regex`, `--fuzzy` (Levenshtein retry on miss), `--in `, `--package `, `--limit` (50). Bare words match case-insensitively or as camel humps (`HMap` finds `HashMap`).| |`jdx resolve `|"What is this symbol?"|`--limit` (50). Several candidates are success (exit 0); none is exit 1 with did-you-mean.| |`jdx ls [package-glob]`|External library browser (packages/types)|`--limit` (50). A glob lists packages with type counts; an exact package lists its types.| |`jdx tree [artifact-glob]`|External library browser (artifacts)|`--depth` (8; 0 = top segments), `--counts`, `--limit` (50)| ## Graph: usages, hierarchy, calls, samples |Command|IDE equivalent|Key flags| |---|---|---| |`jdx usages `|Find usages (`Alt+F7`)|`--kind all\|call\|read\|write\|ref\|new\|throw\|annotation` (`impl`/`override` redirect to `hierarchy`/`implementors`; `--context` redirects to `samples`), `--in` / `--exclude `, `--limit` (50), `--src ` (repeatable source-dir roots). `new` = constructor call sites; `throw` = methods declaring the type in `throws` (declaration-level, not `ATHROW` throw sites); `annotation` = annotated classes/members. Source-dir hits are textual mentions (`ref` only).| |`jdx hierarchy `|Type hierarchy (`Ctrl+H`)|`--up` / `--down` (default both), `--direct`, `--depth N`, `--in` / `--exclude`, `--limit` (50). Member refs exit 3.| |`jdx implementors `|Who implements this?|Alias for `hierarchy --down`: `--direct`, `--depth N`, `--in` / `--exclude`, `--limit` (50)| |`jdx callers `|Call hierarchy in (`Ctrl+Alt+H`)|`--depth N` (default 1), `--in` / `--exclude`, `--limit` (50). Methods + constructors only; type/field refs exit 3. Cycle-safe (`…(cycle)`). Exact name+descriptor matching — not override-aware (see limitations).| |`jdx calls `|Call hierarchy out|Same as `callers`, plus `--external-only` (hide callees in the method's own artifact — dependency view)| |`jdx samples `|*(no IDE equivalent)*|`--limit` (default 3), `--in` / `--exclude`, `--prefer-sources` (rank snippet-capable callers first). Ranked by exemplariness: non-test before test, non-generated before generated, fuller overloads first. Snippets capped at 15 lines. Type refs match calls to any member; field refs exit 3.| ## Upgrades: API diff |Command|IDE equivalent|Key flags| |---|---|---| |`jdx diff `|"What did this upgrade break?"|`--visibility public\|all` (default `public`: public + protected declarations; `all` = every declared member, for refactors and mod/mixin work), `--include-synthetic`, `--severity all\|suspicious\|breaking` (cumulative), `--fail-on none\|breaking\|any`, `--limit` (200), `--fetch`, `--repo`, `--json`. Each of the two specs is a jar, class directory, glob, or `group:artifact:version`. Reads bytecode only — no sources, no decompilation, no classpath. Every finding names one rule (`MEMBER_REMOVED`, `RETURN_TYPE_CHANGED`, `KOTLIN_SUSPEND_CHANGED`, …) at `breaking` / `suspicious` / `info`, and every line is a canonical ref. The full rule set is `docs/PROPOSAL.md` §7.7.| ```console $ jdx diff gson-2.11.0.jar gson-2.14.0.jar --fail-on breaking api diff gson-2.11.0.jar -> gson-2.14.0.jar (public surface) info FIELD_CONSTANT_VALUE_CHANGED com.google.gson.internal.GsonBuildConfig#VERSION: "2.11.0" -> "2.14.0" 0 breaking · 0 suspicious · 17 informational · 2 types added · 0 types removed fail-on breaking: no breaking changes (exit 0) source: gson-2.11.0.jar (bytecode) source: gson-2.14.0.jar (bytecode) next: jdx members com.google.gson.internal.GsonBuildConfig ``` `diff` exits **0 whenever the comparison ran**, whatever it found — the answer is in the report, not in the process status. `--fail-on breaking` (or `any`) is the CI gate and is the only way a successful diff exits non-zero (see the exit-code table). It needs no workspace, no `--jars` and no `--no-daemon`, and is answered by the MCP tool `jdx_diff` and `GET /v1/diff?query=&new=` with identical bytes. ## Environment and maintenance |Command|Purpose|Key flags / notes| |---|---|---| |`jdx version`|Print the version|`--json` for the envelope form| |`jdx upgrade`|Self-update from GitHub Releases (release installs only)|`--version ` (default latest), `--check` (report only), `--repo OWNER/NAME`, `--json`. Exits 1 on unknown tag, 3 on non-release installs, 5 on download failure| |`jdx doctor`|Self-diagnosis|One `ok`/`warn`/`fail` row per check: JDK, `jrt:/`, `javap`, JDK sources, cache, config, index DB, Kotlin sidecar, AppCDS archive (present/absent/stale via size + mtime vs the fat jar; missing/stale degrades via `-Xshare:auto`, never fails), daemon (probed live: running vs stale), workspace, agent setup (OpenCode project/system wiring + detected `v1`/`v2` line via `opencode --version`; Claude Code `.mcp.json`/`~/.claude.json` wiring + `claude --version` presence; Cursor `.cursor/mcp.json`/`~/.cursor/mcp.json` wiring + `cursor-agent --version` presence; Kilo Code project/system wiring; Cline global `cline_mcp_settings.json` wiring, both scopes; Codex CLI `.codex/config.toml`/`~/.codex/config.toml` wiring + `codex --version` presence; GitHub Copilot CLI `.mcp.json`/`~/.copilot/mcp-config.json` wiring + `copilot --version` presence; Antigravity `.agents/mcp_config.json`/`~/.gemini/config/mcp_config.json` wiring + `agy --version` presence). Exits 6 if any check fails.| |`jdx setup --agent opencode\|claude-code\|cursor\|kilo\|cline\|codex\|copilot\|antigravity --scope project\|system`|Wire jdx into an agent|OpenCode: writes the `jdx mcp` MCP entries into `opencode.json`/`opencode.jsonc` (project checkout or `~/.config/opencode`): both the v1 entry (`mcp.jdx`) and the v2 entry (`mcp.servers.jdx`, `enabled` → `disabled`) so either OpenCode line picks it up (v1 support is deprecated, slated for removal). Claude Code: writes the `mcpServers.jdx` entry (`{"command": "jdx", "args": ["mcp"]}`, the `claude mcp add jdx -- jdx mcp` equivalent) into `.mcp.json` (project checkout) or `~/.claude.json` (system). Cursor: writes the same `mcpServers.jdx` shape (`{"command": "jdx", "args": ["mcp"]}`, recognised by `agent mcp list` in `cursor-agent 2026.09.26-dd393fe`) into `.cursor/mcp.json` (project checkout) or `~/.cursor/mcp.json` (system). Kilo Code (an OpenCode fork sharing the `mcp` map shape): writes the single `mcp.jdx` entry into `kilo.json`/`kilo.jsonc` (project root or `.kilo/` — the `.kilo/` variant wins when both exist — or `~/.config/kilo`). Cline: writes the single `mcpServers.jdx` transport entry (`{"transport": {"type": "stdio", "command": "jdx", "args": ["mcp"]}}`, the `cline mcp add jdx -- jdx mcp` equivalent) into the single global `~/.cline/data/settings/cline_mcp_settings.json` for either scope (verified against cline 3.0.65 + extension 4.1.21 — Cline keeps no project-level MCP file). Codex CLI: writes the `[mcp_servers.jdx]` table (`command = "jdx"`, `args = ["mcp"]`, the `codex mcp add jdx -- jdx mcp` equivalent) into `.codex/config.toml` (project checkout, trusted projects) or `~/.codex/config.toml` (system, `$CODEX_HOME` honoured). GitHub Copilot CLI: writes the `mcpServers.jdx` entry (`{"type": "local", "command": "jdx", "args": ["mcp"]}`, the `copilot mcp add jdx -- jdx mcp` equivalent, verified against `GitHub Copilot CLI 1.0.88`) into `.mcp.json` (project checkout — the nearest one walking up, `.github/mcp.json` as fallback — trusted directories) or `~/.copilot/mcp-config.json` (system, `$COPILOT_HOME` honoured). Antigravity: writes the `mcpServers.jdx` entry (`{"command": "jdx", "args": ["mcp"]}`, the `agy mcp add jdx -- jdx mcp` equivalent, verified against `agy` 1.3.0) into `.agents/mcp_config.json` (project checkout — the nearest one walking up) or `~/.gemini/config/mcp_config.json` (system). Reports the detected line. Merges, never clobbers; `--check` reports without writing (exit 1 when absent); `--remove` uninstalls cleanly; re-runs are no-ops. Restart the agent after changing config (it loads once at startup).| |`jdx ws create\|list\|info\|remove\|use\|add`|Project \& library configuration|`create --jars … --src … --coord … --repo … [--jdk\|--no-jdk]`; `add `; `use [--clear]` sets the default; `list` / `info ` / `remove `| |`jdx cache info\|gc\|clear`|Index and cache maintenance|`info` (DB location, size, schema, counts); `gc [--dry-run] [--cache-dir …]` (evicts unreferenced + stale artifacts + orphan daemon logs); `clear` (wipes regenerable cache)| |`jdx kotlin install`|Fetch the Kotlin PSI sidecar|`--repo …` (repeatable), `--force` (re-download). 7 jars, SHA-1 verified, into `/kotlin/` (`~/.cache/jdx/kotlin/` on Linux; per-OS defaults in `docs/PROPOSAL.md` §17.1); present jars are skipped, re-runs resume.| |`jdx help [--agent] [--json]`|Cheat sheet|`--agent` prints the paste-ready agent block| |`jdx bench`|Benchmark the read path|`--iterations N` (default 3, median reported), classpath flags. Fixed `load`/`show`/`members`/`search`/`hierarchy` workload with §15 advisory targets; always exits 0 on success. Falls back to the local `minecraft-client.jar` when no roots are given.| ## Serving: daemon, MCP, HTTP, batch |Command|Purpose|Notes| |---|---|---| |`jdx daemon start\|stop\|status\|restart`|Warm background JVM|Per-workspace unix socket under the OS runtime dir (`$XDG_RUNTIME_DIR/jdx/-v1.sock` on Linux; macOS `$TMPDIR/jdx-$UID`, Windows `%LOCALAPPDATA%/jdx/run` — full table in `docs/PROPOSAL.md` §17.1; a missing `XDG_RUNTIME_DIR` falls back, never `exit 3`); over-long socket paths exit 3 with the `JDX_RUNTIME_DIR` hint (104 B macOS / 108 B elsewhere); concurrent `start` fails fast on the socket lock; `stop` never kills a reused pid; `--idle 5m` default (0 disables); `start` is idempotent. `status`: uptime, memory, query count. `run` (foreground) is the internal spawn target.| |`jdx mcp [-w name]`|MCP stdio server|One typed `jdx_*` tool per query (21 tools) with generated schemas; per-call workspace override; answers byte-identical to `--json`.| |`jdx serve [-w name] [--port 7070] [--bind 127.0.0.1]`|Local HTTP/JSON API|`GET /v1/?query=…&=…`, `POST /v1/batch` (NDJSON), `GET /v1/health`. Localhost by default; blocks until interrupted. Bodies byte-identical to `--json`.| |`jdx batch`|Many queries, one process|NDJSON `RpcRequest` lines on stdin (`{"command":"members","query":"…","params":{…}}`), one envelope per line on stdout; exit code is the max query exit code. Roots resolved once. `--json` accepted and ignored (output is always envelopes).| ## Classpath flags (every query command) `--jars ` (repeatable, merged in front of the workspace) · `--coord group:artifact:version` (repeatable; local `~/.gradle/caches` + `~/.m2` first) · `--repo ` (repeatable Maven mirror, tried before Central) · `--fetch` (allow downloads incl. `-sources.jar`, checksum-verified into `/m2/` (`~/.cache/jdx/m2/` on Linux)) · `--no-jdk` (exclude the running JDK stdlib, included by default via `jrt:/` + `src.zip`) · `-w/--workspace ` (also `JDX_WORKSPACE` env or `jdx ws use` default; Gradle/Maven project roots auto-discover from the working directory) · `--src ` (only `usages`, `batch`, `bench`, and `ws create`: source dirs scanned textually). Global output flags: `--json` (accepted before or after the subcommand, e.g. `jdx --json show …`), `--no-color` (piped output is always plain), `--no-daemon` (force in-process). Warm (daemon) text is always plain; `--json` bytes are identical cold and warm. ## Exit codes and symbol references |Code|Meaning| |---|---| |`0`|Success, results found| |`1`|Valid query, **no results**| |`2`|**Ambiguous** reference — candidates printed, retry with one| |`3`|Usage / argument error| |`4`|No index or workspace resolved for this query| |`5`|Artifact read error (corrupt jar, unreadable file)| |`6`|Internal error| The one exception is `jdx diff`'s `--fail-on breaking\|any` gate, which exits `1` when it trips: a successful comparison otherwise always exits `0`, and the verdict is in the envelope as `result.gate` too. References are Javadoc style — `com.example.Outer`, `com.example.Outer#method(Type)`, short `Outer#method` (resolved, or exit 2 with candidates). Always quote the ref: `#` and `(` are shell-hostile. `::` and `.` are accepted in place of `#`. Constructors are ``, static initialisers ``. Every `--json` answer uses one envelope: `{"jdx":1, "ok":…, "command":…, "query":…, "result":…, "truncated":…, "warnings":…, "provenance":…}`. Warnings carry stable codes (`DUPLICATE_FQN`, `CORRUPT_CLASS`, `SOURCES_VERSION_MISMATCH`, …); provenance names the artifact, the source (sources / bytecode / decompiled + engine), and the file:lines. ## Kotlin note Kotlin signatures come from `@Metadata` (suspend, properties, default args, `@JvmName`); Kotlin member bodies and KDoc come from `.kt` sources through a side-loaded `kotlin-compiler-embeddable` (never on the compile classpath, never in the fat jar). Without the sidecar, `body`/`source`/`doc` over `.kt`-only roots degrade to decompile/`javap` — they emit *something* and say what it is, never an error. Run `jdx kotlin install` to fetch the sidecar set (SHA-1 checked) from Maven Central; `jdx doctor` reports its status. File facades stay JVM-projected and nullability is not rendered (see limitations). --- # jdx — An IDE for Agents **Proposal / design specification — v1.0** Date: 2026-09-13 · Status: **approved scope, pending implementation** --- ## Table of contents 1. [Problem statement](#1-problem-statement) 2. [What an IDE actually gives a human](#2-what-an-ide-actually-gives-a-human) 3. [What an agent needs that an IDE does not provide](#3-what-an-agent-needs-that-an-ide-does-not-provide) 4. [Product goals and non-goals](#4-product-goals-and-non-goals) 5. [Conceptual model](#5-conceptual-model) 6. [Symbol reference grammar](#6-symbol-reference-grammar) 7. [Command surface](#7-command-surface) 8. [Output design](#8-output-design) 9. [Architecture](#9-architecture) 10. [The index](#10-the-index) 11. [Sources, bytecode and decompilation](#11-sources-bytecode-and-decompilation) 12. [Kotlin support](#12-kotlin-support) 13. [Classpath resolution](#13-classpath-resolution) 14. [Interfaces: CLI, MCP, daemon, HTTP](#14-interfaces-cli-mcp-daemon-http) 15. [Performance plan](#15-performance-plan) 16. [Error handling, exit codes, degradation](#16-error-handling-exit-codes-degradation) 17. [Security and safety](#17-security-and-safety) 18. [Testing strategy](#18-testing-strategy) 19. [Dependencies and licensing](#19-dependencies-and-licensing) 20. [Delivery plan / milestones](#20-delivery-plan--milestones) 21. [Deferred to phase 2](#21-deferred-to-phase-2) 22. [Risks](#22-risks) 23. [Appendix A: worked agent session](#appendix-a-worked-agent-session) 24. [Appendix B: full flag reference](#appendix-b-full-flag-reference) --- ## 1. Problem statement When an AI agent works on a JVM project, most of its wall-clock time and most of its context window are consumed not by writing code but by **discovering what the code around it can do**. The current state of the art for an agent investigating a library is a sequence like: ```bash unzip -l ~/.gradle/caches/.../gson-2.14.0.jar | grep -i gson unzip -p ~/.gradle/caches/.../gson-2.14.0-sources.jar com/google/gson/Gson.java | head -200 javap -p -cp gson-2.14.0.jar com.google.gson.Gson javap -c -p -cp gson-2.14.0.jar com.google.gson.Gson | sed -n '/toJson/,/^$/p' grep -rn "TypeAdapter" src/ ``` Every step of that is a problem: |Symptom|Cost| |---|---| |`unzip -p ... \| head -200` on a 1,400-line file|Either the answer is cut off, or 1,400 lines of mostly-irrelevant code land in context| |`javap` prints erased signatures (`java.util.List` not `List`)|The agent reasons about the wrong types| |`javap -c` prints opcodes|The agent must mentally decompile to understand behaviour| |Inherited members are invisible|The agent must manually walk `extends`/`implements` chains, one `javap` per level, to answer "what can I call on this?"| |No sources jar|The agent gives up or guesses the API| |"Who calls this?"|`grep` across jars is impossible; across source it's noisy and misses dynamic/compiled uses| |Every question costs a new shell round-trip|Latency and token cost multiply| The underlying issue: **an agent is doing by hand what an IDE does with an index.** IntelliJ solved this for humans twenty years ago. Nothing exposes it to a process that speaks stdin and stdout. `jdx` is that exposure. --- ## 2. What an IDE actually gives a human Enumerating this explicitly matters, because the goal is parity — not "a nicer `javap`". IntelliJ IDEA provides, over any jar on the classpath, with or without sources: |IDE capability|Human gesture|`jdx` equivalent| |---|---|---| |Code completion on a receiver|type `foo.`|`jdx members --inherited`| |Parameter info|`Ctrl+P` inside `(`|`jdx signature '#'`| |Quick documentation|`Ctrl+Q` / `F1`|`jdx doc ''`| |Go to declaration|`Ctrl+Click`|`jdx show ` / `jdx body ''`| |Go to implementation|`Ctrl+Alt+Click`|`jdx hierarchy --implementors`| |Type hierarchy|`Ctrl+H`|`jdx hierarchy `| |Call hierarchy|`Ctrl+Alt+H`|`jdx callers` / `jdx calls`| |Find usages|`Alt+F7`|`jdx usages `| |Search everywhere / Go to class / symbol|`Shift Shift`, `Ctrl+N`, `Ctrl+Alt+Shift+N`|`jdx search `| |File structure popup|`Ctrl+F12`|`jdx outline `| |Decompile a class without sources|open a `.class`|`jdx body` / `jdx source` (Vineflower)| |Attach/browse sources jar|automatic|automatic (sources jar detection)| |Show inherited members in completion|default on|`--inherited`, default on| |External library browser|Project view → External Libraries|`jdx ls` / `jdx tree`| |Show bytecode|*Show Bytecode* action|`jdx body --engine javap`| |Analyze dependencies of a class|*Analyze → Dependencies*|`jdx calls --depth N`| All of the above are in scope for v1 except where §21 says otherwise. --- ## 3. What an agent needs that an IDE does not provide Parity is the floor, not the ceiling. An agent is not a human and has different constraints. These features have **no IDE equivalent** and exist purely because the consumer is a language model: ### 3.1 Context-window economy An IDE renders into a scrollable window; wasted pixels are free. For an agent, every token is paid for and crowds out reasoning. So: - **`--max-lines` / `--brief`** — bounded output with an explicit continuation hint: `… 47 more methods. Narrow with --grep or --from .` - Signature-only modes that omit bodies and docs by default. - **No decorative output.** No banners, no box drawing, no repeated file paths. - Deduplicated overload rendering where the difference is only one parameter. ### 3.2 Machine-legible failure A human reads "not found" and adapts. An agent needs a branch it can take without parsing prose: distinct **exit codes** for *not found*, *ambiguous*, *no index*, *bad usage* (§16), and an `--json` envelope that carries the same information structurally. ### 3.3 Never-guess semantics IntelliJ can pop a chooser dialog. A CLI cannot. So an ambiguous reference is a **first-class result**, not an error: `jdx` exits `2` and prints every candidate as an exact, copy-pasteable canonical reference. The agent's retry is then mechanical. ### 3.4 Self-describing next actions Every rendered symbol carries the exact reference string needed to query it further. An agent should never have to *construct* a reference by string surgery — it copies one it was handed. ### 3.5 Provenance and confidence The agent must know whether it is reading ground truth or a reconstruction: ``` source: gson-2.14.0-sources.jar (com/google/gson/Gson.java:412-430) ``` versus ``` source: decompiled by vineflower from gson-2.14.0.jar (no sources jar) ⚠ reconstructed ``` An agent that knows a body is decompiled will not quote it as canonical in a code review. An agent that does not, will. ### 3.6 Real usage examples `jdx samples ''` returns **actual call sites from the indexed classpath**, trimmed to a few lines of surrounding context. "How is this API really used?" answered with evidence rather than the model's recollection of a README. This is often more valuable than the javadoc, and no IDE surfaces it in this form. ### 3.7 Batch execution JVM start-up amortized across many questions: ```bash jdx batch --json <<'Q' members com.google.gson.Gson --inherited body com.google.gson.Gson#toJson(Object) usages com.google.gson.TypeAdapter --limit 10 Q ``` One process, one index load, N results as JSON lines. Cuts a 10-question investigation from ~4 s of process start-up to ~0.4 s. ### 3.8 A self-installing instruction block `jdx help --agent` prints a compact, paste-ready cheat sheet designed to be dropped into a `CLAUDE.md`, system prompt, or MCP tool description — so an agent learns the tool without the user writing documentation. ### 3.9 Idempotent, cacheable, deterministic Identical output for identical input means the *agent's own* prompt cache stays warm across turns, and golden-file testing is possible. --- ## 4. Product goals and non-goals ### Goals - **G1** Answer any "what is this / what can I call / what does it do / who uses it" question about JVM code on a classpath, in one command, in minimal tokens. - **G2** Work with sources jars, plain jars, class dirs, source dirs, Maven coordinates, and the JDK itself — uniformly, with no configuration in the common case. - **G3** Resolve inherited members transitively so an agent never walks a hierarchy by hand. - **G4** Be fast enough to be called dozens of times in a single agent task. - **G5** Be equally usable from a shell, from MCP, and over HTTP. - **G6** Be honest: always state provenance, never fabricate, never silently truncate. ### Non-goals - **N1** Not a compiler, type-checker, or language server for *your* code being edited. (`jdx` reads a classpath; it does not do incremental compilation or diagnostics.) - **N2** Not a refactoring or code-modification tool. **Strictly read-only** with respect to user code. - **N3** Not a build tool. It reads Gradle/Maven *outputs and caches*; it does not run builds or resolve full dependency graphs with conflict resolution. - **N4** Not a general decompiler UI. Decompilation is a means to answer questions. - **N5** Not multi-language. JVM only (Java, Kotlin, and the JVM projection of anything else — Scala/Groovy/Clojure classes are readable as bytecode, but get no language-specific view). --- ## 5. Conceptual model Four nouns. Everything in the tool is expressed in terms of them. ### 5.1 Root A place where code can be found. Exactly six kinds: |Root kind|Example|Provides| |---|---|---| |`BinaryJar`|`gson-2.14.0.jar`|classes (bytecode)| |`SourcesJar`|`gson-2.14.0-sources.jar`|`.java` / `.kt` text| |`ClassDir`|`build/classes/java/main`|classes (bytecode)| |`SourceDir`|`src/main/kotlin`|`.java` / `.kt` text| |`JrtModules`|the running JDK via `jrt:/`|JDK classes (+ `lib/src.zip` for JDK sources)| |`MavenCoord`|`com.google.code.gson:gson:2.14.0`|resolved to a `BinaryJar` + `SourcesJar` pair| ### 5.2 Artifact A `BinaryJar` **paired with** its matching `SourcesJar` when one can be found, identified by a **content hash** of the binary. Pairing rules, in order: 1. Sibling file `-sources.jar`. 2. Gradle cache layout: same `modules-2/files-2.1////*/` parent. 3. Maven cache layout: same directory in `~/.m2/repository`. 4. Explicit `--sources ` override. 5. Sources embedded in the binary jar itself (some artifacts ship `.java` alongside `.class`). The content hash is the cache key, so an artifact indexed once is reused by every workspace that contains it. Re-indexing `gson` for a second project is free. ### 5.3 Workspace A named, ordered list of roots plus its resolution settings. Workspaces are how an agent avoids passing a 2,000-entry classpath on every invocation: ```bash jdx ws create mc --jars '~/.gradle/caches/**/*.jar' --src ./src/main/java --jdk jdx -w mc members net.minecraft.world.item.ItemStack ``` Ordering matters: earlier roots shadow later ones, exactly like a JVM classpath, so `jdx` reports the class the JVM would actually load and **warns on duplicate FQNs across artifacts** (`shaded`/`relocated` jars are a real and frequent source of agent confusion). Stored as TOML in `/workspaces/.toml` (`~/.config/jdx/...` on Linux; per-OS config defaults in §17.1). Human-editable and diffable. ### 5.4 Symbol A type, method, field, constructor, package, or module — each with a **canonical reference** (§6), a declaring artifact, a kind, and a resolved signature. The symbol is the unit every command consumes and emits. --- ## 6. Symbol reference grammar Designed to be **generous on input, canonical on output**. ``` ref := typeRef [ memberSep memberRef ] typeRef := [ coord '/' ] [ package '.' ] simpleName { ('$'|'.') nestedName } coord := group ':' artifact ':' version (Maven coordinate prefix) memberSep := '#' | '::' | '.' memberRef := ( name | '' | '' ) [ params ] params := '(' [ paramList ] ')' [ ':' returnType ] | descriptor (exact JVM descriptor) paramList := param { ',' param } param := simpleTypeName | fqTypeName | primitiveName | param '[]' | '...' ``` ### Accepted forms, all valid |Input|Meaning| |---|---| |`Gson`|short type name → resolve across workspace; exit `2` if ambiguous| |`com.google.gson.Gson`|exact type| |`com.google.gson.Gson$1`|anonymous class| |`java.util.Map.Entry`|nested via `.` (normalizes to `Map$Entry`)| |`com.google.code.gson:gson:2.14.0/com.google.gson.Gson`|coordinate-scoped| |`Gson#toJson`|all overloads of `toJson`| |`Gson#toJson(Object)`|one overload, simple param names| |`Gson#toJson(java.lang.Object)`|one overload, fully qualified| |`Gson#toJson(Object):String`|disambiguated by return type (needed for bridge methods)| |`Gson#toJson(Ljava/lang/Object;)Ljava/lang/String;`|exact descriptor — never ambiguous| |`Gson::toJson`|`::` separator| |`Gson#(Excluder,FieldNamingStrategy)`|constructor| |`Gson#excluder`|field| |`com.google.gson.*`|package glob (for `search`, `ls`)| ### Canonical output form `jdx` always *prints* the most specific unambiguous form: `com.google.gson.Gson#toJson(java.lang.Object)` adding `:returnType` only when required to disambiguate bridge/covariant overloads. ### Ambiguity protocol ``` $ jdx body 'Gson#toJson' ambiguous: 4 candidates for Gson#toJson com.google.gson.Gson#toJson(java.lang.Object) com.google.gson.Gson#toJson(java.lang.Object, java.lang.reflect.Type) com.google.gson.Gson#toJson(com.google.gson.JsonElement) com.google.gson.Gson#toJson(java.lang.Object, java.lang.Appendable) hint: re-run with one of the refs above $ echo $? 2 ``` --- ## 7. Command surface ### 7.1 Inspection — the core loop #### `jdx show ` The "class card". Kind, modifiers, generic declaration, supertype chain, interfaces, enclosing/nested types, annotations, deprecation, origin artifact, source availability, Kotlin-ness, and a **member count summary** (not the members themselves). ``` class com.google.gson.Gson package com.google.gson · gson-2.14.0.jar · sources: yes · java public final class Gson extends java.lang.Object nested: Gson$FutureTypeAdapter, Gson$1 members: 39 methods (14 public), 12 fields (0 public), 2 constructors doc: This is the main class for using Gson. Gson is typically used by first constructing a Gson instance and then invoking toJson or fromJson… next: jdx members com.google.gson.Gson --inherited ``` #### `jdx members ` **The most important command** — the `.` completion equivalent. - `--inherited` *(default: on)* — include members from superclasses and interfaces, grouped and labelled by declaring type, with overridden members collapsed. - `--declared` — only members declared on this exact type. - `--kind method|field|ctor|all`, `--static`, `--instance` - `--access public|protected|package|private|all` *(default: `public` + `protected`)* - `--from ` — only members inherited from a specific supertype - `--grep ` — filter by name - `--with-doc` — include first javadoc sentence per member - `--brief` — signatures only: bare member rows, no group headers, no provenance block (warnings and footers stay); mutually exclusive with `--with-doc`; text only, `--json` unaffected - `--max-lines N` — cap text lines with a continuation footer; text only, `--json` unaffected - `--sort name|kind|declaring` *(default: `kind` then `name`)* - `--include-synthetic` — bridge/synthetic/lambda members, hidden by default Overloads of the same name are grouped. Inherited `java.lang.Object` members are collapsed to one line by default (`+ 9 from java.lang.Object (--from java.lang.Object to expand)`) because they are almost never what the agent wants and cost ~200 tokens every call. #### `jdx signature ` Just the signature(s), with real parameter names, generics, throws, annotations, and default values. The "parameter info" popup. One line per overload. #### `jdx doc ` Javadoc/KDoc only, rendered to plain text: HTML stripped, `{@link}`/`{@code}` unwrapped, `@param`/`@return`/`@throws`/`@since`/`@deprecated` preserved as a tidy block. **Inherited documentation is pulled from the nearest supertype** that documents the method (matching IntelliJ's quick-doc), labelled as such. #### `jdx body ` The headline feature: *"give me the body of `org.some.lib.AClass.someMethod(int,int)`"*. Resolution order: 1. Sources jar/dir → exact AST node range → verbatim text (**ground truth**). 2. No sources → Vineflower decompilation of just the enclosing class, method extracted. 3. `--engine javap` → raw JVM instructions. Flags: `--engine vineflower|javap`, `--with-doc`, `--with-signature`, `--context N` (surrounding lines), `--line-numbers`, `--max-lines N`. Also accepts a **type** ref, in which case it prints the whole type body — which is exactly `jdx source` with the type's range. #### `jdx source ` Whole source file or a slice: `--lines 100:180`, `--around --context 20`. Decompiles if no sources exist. This is the escape hatch when a body is not enough. #### `jdx outline ` One dense line per member — the *File Structure* popup. Cheapest way to see a class's shape. Takes the same `--brief` / `--max-lines` text budgets as `members`. ### 7.2 Search and navigation #### `jdx search ` Symbol search across the workspace. - Matching: glob (`*Http*Client`), regex (`--regex`), **camel-hump** (`HMap` → `HashMap`, `gJson` → `getJson`) — IntelliJ's `Ctrl+N` behaviour. - `--kind class|interface|enum|record|annotation|method|field|package|module` - `--in `, `--package `, `--limit N` *(default 50)* - `--fuzzy` — Levenshtein fallback when nothing matches exactly #### `jdx resolve ` "What is this identifier?" — takes an unqualified or partial name and lists every candidate with its kind and artifact. The disambiguator an agent reaches for when it sees an unknown symbol in a stack trace or a code snippet. #### `jdx imports ` The import statement(s) to add, plus a warning when several artifacts provide the same FQN. #### `jdx ls []` / `jdx tree ` Browse an artifact's packages and types. `--depth N`, `--counts`. ### 7.3 The relationship graph #### `jdx hierarchy ` Supertypes upward (full transitive chain, interfaces included) and subtypes/implementors downward across the whole workspace. `--up`, `--down`, `--depth N`, `--direct` (one level), `--in `. ``` java.util.Map ↑ (none) ↓ implementors in workspace (37, showing 10) java.util.HashMap java.base java.util.LinkedHashMap java.base (extends HashMap) java.util.TreeMap java.base com.google.gson.internal.LinkedTreeMap gson-2.14.0.jar … ``` #### `jdx usages ` Find Usages across **all workspace jars plus the project's own source dirs**. - `--kind call|ref|impl|override|read|write|new|throw|annotation|all` - `--in `, `--exclude `, `--limit N`, `--context N` - Groups by artifact, then by declaring method, with the canonical ref of each call site. - `--kind throw` matches `throws` declarations (declaration-level), not `ATHROW` throw sites. #### `jdx callers ` / `jdx calls ` Incoming / outgoing call edges. `--depth N` walks the tree (cycle-safe, with `…(cycle)` markers). This lets an agent trace an execution path across jars without decompiling every class along the way. #### `jdx samples ` Real call sites as usage examples, source-rendered when sources are available, ranked by "exemplariness" (prefers non-test, non-generated, short enclosing methods, and call sites that use more of the API's parameters). `--limit N` *(default 3)*. #### `jdx overrides ` / `jdx implementors ` Convenience aliases over `hierarchy`/`usages` because they are so frequently wanted. ### 7.4 Workspace and cache management ``` jdx ws create [--jars …] [--src …] [--coord …] [--jdk] jdx ws list | info | remove | add | use jdx index [-w ] [--force] build / refresh the index jdx cache info | gc | clear jdx kotlin install [--repo ] [--force] fetch the Kotlin PSI sidecar set jdx doctor environment diagnostics jdx version [--json] jdx help --agent paste-ready cheat sheet for an agent prompt ``` ### 7.5 Serving ``` jdx mcp MCP server over stdio jdx daemon start|stop|status|restart background index host, 5-min idle auto-shutdown jdx serve [--port 7070] [--bind 127.0.0.1] HTTP/JSON API jdx batch [--json] read queries from stdin, one result per line ``` ### 7.6 Global flags `-w/--workspace`, `--jars`, `--src`, `--coord`, `--jdk/--no-jdk`, `--json`, `--no-color`, `--max-lines`, `--brief`, `--verbose`, `--quiet`, `--no-daemon`, `--timeout`, `--cache-dir`. ### 7.7 Public-API diff #### `jdx diff ` Answers the question a dependency upgrade poses: *what changed, and can I still compile and link against it?* It compares the API surface of two artifacts — jars, class directories, globs, or Maven coordinates (`--fetch` to download) — and reports every difference, **most severe first**, each naming one rule. ``` jdx diff gson-2.11.0.jar gson-2.14.0.jar api diff gson-2.11.0.jar -> gson-2.14.0.jar (public surface) breaking MEMBER_REMOVED com.google.gson.stream.JsonReader#getLenient() suspicious CHECKED_EXCEPTION_ADDED com.google.gson.Gson#toJson(java.lang.Object): java.io.IOException info MEMBER_ADDED com.google.gson.stream.JsonReader#getNestingLimit() 1 breaking · 1 suspicious · 15 informational · 2 types added · 0 types removed source: gson-2.11.0.jar (bytecode) source: gson-2.14.0.jar (bytecode) next: jdx members com.google.gson.stream.JsonReader ``` Flags: `--visibility public|all` (default `public`; `all` compares every declared member, for refactors and mod/mixin work), `--include-synthetic`, `--severity all|suspicious|breaking`, `--fail-on none|breaking|any`, `--limit N` *(default 200)*, `--fetch`, `--repo`, `--json`, `--no-color`. **Structure only.** A diff reads bytecode and nothing else: no sources jar, no decompilation, no classpath. An API has no "flesh" to recover — the whole surface is in the class files, and the truth model of §11.1 already says structure comes from bytecode. **What is compared.** Types and their `kind`, visibility, supertypes and annotations; members by **name plus erased parameter list**, so a covariant return is one `RETURN_TYPE_CHANGED` rather than a removal plus an addition. Field identity includes the field type. `` is never part of an API. **Three severities, one rule each.** Every finding carries exactly one rule id — the `CompatRule` name, so an agent filters on a code, never on prose: - **BREAKING** — an already-compiled caller can fail to link or to run: `TYPE_REMOVED`, `TYPE_KIND_CHANGED`, `TYPE_VISIBILITY_NARROWED`, `TYPE_MADE_FINAL`, `SUPERTYPE_REMOVED`, `MEMBER_REMOVED`, `MEMBER_VISIBILITY_NARROWED`, `MEMBER_MADE_FINAL`, `STATIC_TO_INSTANCE`, `INSTANCE_TO_STATIC`, `ABSTRACT_ADDED`, `INTERFACE_METHOD_ADDED`, `ENUM_CONSTANT_REMOVED`, `PARAMETER_TYPE_CHANGED`, `RETURN_TYPE_CHANGED`, `FIELD_TYPE_CHANGED`. - **SUSPICIOUS** — binary-compatible by the letter of JLS §13.5, but recompiling, the Kotlin view or the behaviour can still break: `MEMBER_MOVED_TO_SUPERTYPE`, `GENERIC_SIGNATURE_CHANGED`, `CHECKED_EXCEPTION_ADDED`, `VARARGS_CHANGED`, `NATIVE_ADDED`/`NATIVE_REMOVED`, `KOTLIN_NAME_CHANGED`, `KOTLIN_SUSPEND_CHANGED`, `KOTLIN_NULLABILITY_CHANGED`, `KOTLIN_DEFAULT_ARG_REMOVED`, `KOTLIN_PARAMETER_NAME_CHANGED`. - **INFO** — additive or cosmetic: `TYPE_ADDED`, `SUPERTYPE_ADDED`, `MEMBER_ADDED`, `FINAL_REMOVED`, `ABSTRACT_REMOVED`, `ANNOTATION_ADDED`/`ANNOTATION_REMOVED`/ `ANNOTATION_VALUES_CHANGED`, `DEPRECATED_ADDED`/`DEPRECATED_REMOVED`, `THROWS_REMOVED`, `PARAMETER_NAME_CHANGED`, `PARAMETER_NAMES_LOST`, `SYNCHRONIZED_ADDED`/`SYNCHRONIZED_REMOVED`, `TRANSIENT_CHANGED`, `FIELD_CONSTANT_VALUE_CHANGED`, `ANNOTATION_DEFAULT_CHANGED`, `KOTLIN_DEFAULT_ARG_ADDED`, `KOTLIN_PROPERTY_BECAME_MUTABLE`. **Three pairing passes, so the output is not a remove/add avalanche.** A member that disappeared *and* one that appeared, agreeing on name and arity, is one retyped signature (`PARAMETER_TYPE_CHANGED`, `FIELD_TYPE_CHANGED`), not two findings. What is left over, agreeing on the *Kotlin* name and the arity a Kotlin caller writes, is a JVM rename (`KOTLIN_NAME_CHANGED` — an `@JvmName` appearing or disappearing, an `internal` member's module-name mangling) or a `suspend` flip (`KOTLIN_SUSPEND_CHANGED`, whose descriptor carries the hidden `Continuation`). The rest are plain removals and additions. **An ambiguous bucket is never paired** — with two removals of one shape there is no way to know which addition replaced which, and guessing is worse than asking (§3.3). **The inheritance check, and where it refuses to guess.** A member gone from its type but declared by one of that type's *new supertypes* is `MEMBER_MOVED_TO_SUPERTYPE`, not a removal: the caller's `invokevirtual` still resolves through the chain. When the walk hits a supertype the artifact does not hold, absence proves nothing — so the removal stays `MEMBER_REMOVED` and the detail says which supertype was missing. **A diff never downgrades a breaking change on a maybe.** **Exit codes.** A successful comparison **exits 0 whatever it found** — "found something" is the answer, not a failure, and an agent must not have to parse prose to learn it. The per-severity tally always describes the whole comparison, even when `--severity` or `--limit` hid part of it. `--fail-on breaking` (or `any`) turns the report into a process status for a pipeline and exits **1**; the verdict is also in the envelope as `result.gate`, so a consumer that cannot see exit codes still sees the answer. Usage errors are 3 and an unreadable artifact is 5, exactly as everywhere else. **No workspace, no `--jars`, no `--no-jdk`, no `--no-daemon`.** A diff names its own two artifacts, so there is no classpath to resolve — it works in an empty directory with no workspace ever created. And it is not forwarded to the daemon: a daemon warm on one workspace's classpath holds nothing a two-artifact comparison can reuse, and a `--fail-on` verdict has no way back over the v1 wire, which carries an exit code only as an error. The daemon still *serves* `diff` — `jdx_diff` over MCP and `GET /v1/diff?query=…&new=…` dispatch the same `JdxService.diff` call and answer byte-identically (§14). --- ## 8. Output design ### 8.1 Text (default) Optimised for a language model reading it: - **Two-space indentation**, never box-drawing or tables — they cost tokens and tokenize badly. - **Leading kind word** on every entity line (`class`, `method`, `field`) so the model never has to infer structure from punctuation. - **Grouping headers** instead of repeating the declaring type on every line. - **A `next:` line** on single-entity output, suggesting the highest-value follow-up command. - **Truncation is always explicit**, always with the flag that would reveal the rest. - **ANSI colour only when stdout is a TTY**; never when piped (agents always get plain text). ### 8.2 JSON (`--json`) A stable envelope, versioned, identical across CLI / HTTP / MCP: ```json { "jdx": 1, "ok": true, "command": "members", "query": "com.google.gson.Gson", "result": { "...": "command-specific" }, "truncated": { "shown": 50, "total": 97, "hint": "--limit 200" }, "warnings": [ {"code": "SOURCES_VERSION_MISMATCH", "message": "sources jar does not match binary"} ], "provenance": [ {"symbol": "com.google.gson.Gson", "artifact": "gson-2.14.0.jar", "origin": "sources", "file": "com/google/gson/Gson.java", "lines": [412, 430]} ] } ``` `ok: false` carries `error.code` matching the exit-code taxonomy, plus `candidates[]` for the ambiguous case — so an HTTP/MCP client gets the same disambiguation protocol as the CLI. ### 8.3 Truncation policy Never cut mid-entity. Prefer dropping whole low-value groups (inherited `Object` members, synthetic members, private members) before dropping high-value ones, and always report what was dropped and how to get it. --- ## 9. Architecture ### 9.1 Modules (Gradle multi-project) ``` core/ Pure Kotlin domain: Symbol/Type/Member model, SymbolRef parser+printer, hierarchy & member-resolution algorithms, rendering (text + JSON), truncation/token-budget logic. No file or network IO. Fully unit-testable. index/ ASM class reading, @Metadata decoding, reference-edge extraction, SQLite store, incremental indexer, artifact hashing & sources pairing. sources/ Sources-jar/dir access, JavaParser integration, Kotlin PSI integration (behind an interface, loaded lazily in an isolated classloader), symbol→(file,range) mapping, body extraction. decompile/ DecompilerEngine interface; Vineflower and javap implementations; single-class decompilation with caching. cli/ Clikt command tree; flag parsing; renderer selection; exit codes; daemon client (transparently forwards to a running daemon). mcp/ MCP stdio server; tool schema generation from the same command metadata the CLI uses (single source of truth — no schema drift). server/ HTTP/JSON server (JDK built-in com.sun.net.httpserver — zero dependency) and the daemon host with idle-timeout supervision. app/ Fat-jar assembly, AppCDS archive generation, `jdx` launcher script, install. ``` **Hard rule:** `cli`, `mcp` and `server` are *adapters*. They contain argument parsing and transport only. All behaviour lives in `core`/`index`/`sources`/`decompile` behind a `JdxService` facade, so the four interfaces cannot drift apart. ### 9.2 The service facade ```kotlin interface JdxService { fun show(ref: TypeRef, opts: ShowOptions): Result fun members(ref: TypeRef, opts: MemberOptions): Result fun body(ref: MemberRef, opts: BodyOptions): Result fun search(pattern: Pattern, opts: SearchOptions): Result fun usages(ref: SymbolRef, opts: UsageOptions): Result fun hierarchy(ref: TypeRef, opts: HierarchyOptions): Result fun callGraph(ref: MemberRef, dir: Direction, opts: CallOptions): Result // … } ``` One implementation over a live index; one implementation that is an RPC client to the daemon. The CLI picks between them transparently. ### 9.3 Member resolution algorithm (the `--inherited` core) This is the piece that saves agents the most work, so it is specified precisely: 1. Load the target type's `ClassInfo` from the index. 2. Compute the **linearized supertype set**: BFS over `superclass` then `interfaces`, recording depth and path. Cycle-safe. Includes `java.lang.Object` last. 3. For each supertype, substitute **generic type arguments** down the chain, so `class StringList extends ArrayList` reports `boolean add(String)`, not `add(E)`. (Signature attribute → type-variable environment → substitution.) 4. Collect members, applying JLS visibility rules from the perspective of the *query*: `private` members of supertypes are excluded; package-private only when same package. 5. **Collapse overrides**: a member with the same name+erased-descriptor as one already collected from a nearer type is dropped, but recorded as `overridden in ` metadata. 6. Drop bridge/synthetic members unless `--include-synthetic`. 7. For Kotlin types, map the collected JVM members back onto Kotlin declarations (properties instead of getter/setter pairs, default-arg synthetics folded in). 8. Group by declaring type, sort, render. --- ## 10. The index ### 10.1 Why persistent "Find usages" and "implementors" require scanning **every class in the workspace** for references to a target. For a Minecraft-scale workspace (~20k classes plus dependencies) that is seconds per query if done live, and milliseconds if done from an index. The index is also what makes camel-hump symbol search viable. ### 10.2 Storage: SQLite, content-hash keyed, globally shared One database: `/index/v1.db` (`~/.cache/jdx/index/v1.db` on Linux; per-OS cache defaults in §17.1) in WAL mode — many readers, one writer. Every row is scoped by `artifact_id`, which is the **content hash of the binary jar**. Consequences, all good: - Indexing `gson` once serves every workspace that contains it. - A workspace is just an ordered list of `artifact_id`s — cheap to create, cheap to switch. - Cache invalidation is trivial: if the hash changed, it is a different artifact. - `jdx cache gc` drops artifacts no workspace references and none was used recently. ### 10.3 Schema (abbreviated) ```sql artifact(id, hash, path, sources_path, kind, jar_mtime, jar_size, indexed_at, schema_ver) class (id, artifact_id, fqn, simple_name, package, access, kind, signature, super_id, source_file, is_kotlin, deprecated, outer_id) iface (class_id, iface_fqn) -- implements edges member (id, class_id, name, descriptor, signature, access, kind, param_names, throws, default_value, deprecated) annot (owner_kind, owner_id, annot_fqn, values) ktmeta (class_id, blob) -- decoded @Metadata doc (owner_kind, owner_id, text, tags) -- javadoc/KDoc extracted from sources srcmap (owner_kind, owner_id, file, start_line, end_line, start_col, end_col) ref (from_member_id, to_fqn, to_member, kind, line) -- the usage/call graph name_idx(simple_name, camel_humps, class_id) -- search acceleration ``` Indices on `class.fqn`, `class.simple_name`, `member.name`, `ref.to_fqn`, `ref.to_member`, `iface.iface_fqn`, `name_idx.camel_humps`. ### 10.4 Indexing pipeline Per artifact, parallel across artifacts using **virtual threads**: 1. Hash the jar (streaming, `SHA-256` truncated to 128 bits — enough, and fast). 2. If `artifact.hash` already present with a matching `schema_ver`, skip entirely. 3. Stream entries; for each `.class`: - `ClassReader` with `SKIP_FRAMES` (we *need* debug info for parameter names, so not `SKIP_DEBUG`). - Extract class/member/annotation rows. - Extract reference edges from the method bodies via a lightweight `MethodVisitor` (`visitMethodInsn`, `visitFieldInsn`, `visitTypeInsn`, `visitInvokeDynamicInsn`, `visitLdcInsn` for class constants) — no full control-flow analysis. - Decode `@Metadata` if present. 4. If a sources jar is paired, index it **lazily on first source query** rather than eagerly — source parsing (especially Kotlin PSI) is an order of magnitude more expensive than ASM, and most queries never need it. Source indexing populates `doc` and `srcmap`. 5. Commit in batches inside one transaction per artifact. **Incremental behaviour:** a changed `ClassDir`/`SourceDir` (the user's own project, which changes constantly) is re-indexed per-file by mtime, not wholesale. ### 10.5 Estimated cost Rough, to be validated against `minecraft-client.jar` during M2: - Index rate target: **≥ 3,000 classes/second** for the bytecode pass on this machine. - Index size target: **≤ 6 %** of jar size (bytecode pass), dominated by the `ref` table. - Query latency target: **\< 15 ms** warm for any single-type query; **\< 150 ms** for `usages` over a 20k-class workspace. --- ## 11. Sources, bytecode and decompilation ### 11.1 The truth model — "bytecode is the skeleton, sources are the flesh" |Fact|Authority| |---|---| |Which members exist|**bytecode** (includes compiler-generated members)| |Modifiers, erased descriptors, generic signatures|**bytecode**| |Supertypes, interfaces, annotations|**bytecode**| |Parameter *names*|sources → else `MethodParameters` attr → else `LocalVariableTable` → else `arg0`| |Method bodies|sources → else decompiled| |Javadoc / KDoc|**sources only** (jars strip it)| |Inline comments|**sources only**| This ordering matters: a sources jar can be **stale or mismatched** (a real and common packaging bug). By taking structure from the binary, `jdx` cannot be lied to about the API. When a member in the binary has no counterpart in the sources — or vice versa — it emits: ``` ⚠ SOURCES_VERSION_MISMATCH: gson-2.14.0-sources.jar declares Gson#toJson(Object, Writer) which is absent from gson-2.14.0.jar. Structure shown from bytecode. ``` Synthetic members present only in bytecode (bridges, `access$000`, lambda bodies, `$VALUES`) are hidden by default rather than reported as a mismatch. ### 11.2 Decompilation **Vineflower** (Apache-2.0, the actively maintained Fernflower fork; already present in the local Gradle cache since Fabric Loom uses it) is bundled and is the default engine. - Decompiles **one class at a time** with the workspace as the classpath context, so generics and inherited-method resolution come out correct. - Decompiled output is **cached** on disk keyed by `(class hash, engine, engine version)` — decompiling a large class costs 50–500 ms, and agents ask for several members of the same class in a row. - Loaded in an **isolated, lazily-created classloader**, so the common path (no decompilation needed) never pays for it. - Method extraction from decompiled text reuses the JavaParser path: the decompiled class is parsed, the requested member's node range is taken. This keeps `jdx body` output identical in shape whether it came from real sources or a decompiler. **javap** (`--engine javap`) shells out to the system `javap` (resolved from `JAVA_HOME`, the running JVM's home, then `PATH`) for cases where the agent wants exact opcodes: verifying a compiler optimisation, reading a `switch` bootstrap, confirming a constant value. Output is passed through with minimal reformatting. **Always labelled.** Decompiled output is never presented as if it were the original source. --- ## 12. Kotlin support Kotlin's JVM projection is *misleading*: `String?` and `String` are the same JVM type, `suspend fun` gains a hidden `Continuation` parameter and returns `Object`, properties become `getX`/`setX` pairs, default arguments become `foo$default` synthetics, and `data class` generates `componentN`/`copy`. An agent reading the JVM view of a Kotlin library **will write wrong code**. ### 12.1 Binaries — `@Metadata` Decoded with `kotlin-metadata-jvm`. `jdx` renders true Kotlin declarations: ``` class kotlinx.coroutines.flow.FlowKt public suspend fun Flow.collect(collector: FlowCollector): Unit public fun flowOf(vararg elements: T): Flow public val Flow.replayCache: List ``` instead of the JVM view (`Object collect(Flow, FlowCollector, Continuation)`). Specifically recovered: nullability, variance, `suspend`, `inline`/`reified`, extension receivers, properties (with backing-field/accessor detail), default arguments, `data`/`sealed` /`value`/`object`/`companion`, type aliases, and `@JvmName` mappings. `--view jvm` forces the Java projection when the agent genuinely needs the JVM truth (e.g. calling Kotlin from Java, or reading a stack trace). ### 12.2 Sources — full PSI parsing Per the user's decision, `.kt` files are parsed with **`kotlin-compiler-embeddable`** into a real PSI tree, rather than by lightweight brace matching. This buys exactness on the hard cases (nested string templates containing braces, multi-line raw strings, trailing lambdas, `when` blocks, expression-bodied functions, nested local functions) and enables accurate Kotlin-aware source-level usages. **Cost and mitigation.** `kotlin-compiler-embeddable` is ~55 MB and takes ~0.6–1.0 s to initialise. Therefore it is: - behind the `sources` module's `SourceParser` interface; - **loaded lazily**, only when a `.kt` file must actually be parsed; - loaded in an **isolated `URLClassLoader`** so its shaded Guava/Intellij-platform classes cannot collide with anything else; - initialised **once per daemon lifetime** — with the daemon running (the normal case for an agent doing repeated queries), the cost is paid once and then never again; - shipped in a **separate jar fetched/verified on first use**, not baked into the main fat jar, so a user who never inspects Kotlin sources never downloads it. (`jdx doctor` reports whether the Kotlin source module is installed.) Java sources use **JavaParser**, which is small and fast and needs none of this. --- ## 13. Classpath resolution Resolution order when a command runs. First match wins; `--jars`/`--src` always merge in. 1. **Explicit flags**: `--jars `, `--src `, `--coord `, `--sources`. 2. **`-w `** — a named workspace. 3. **`JDX_WORKSPACE` env var**, then the workspace set by `jdx ws use`. 4. **Project auto-discovery** — walk up from CWD looking for `settings.gradle(.kts)`, `build.gradle(.kts)`, `pom.xml`, or `.idea/`. On a hit, assemble a workspace from: - the project's own `src/main/{java,kotlin}` and `build/classes/**`; - dependency jars found in `~/.gradle/caches/modules-2/files-2.1/**` and `~/.m2/repository/**` **that the project's lock/metadata files actually reference**, falling back to a broader cache scan with a warning if the reference set can't be determined (we do not run Gradle — see N3); - the JDK. The derived workspace is cached under `/auto/.toml` (`~/.cache/jdx/auto/...` on Linux; per-OS cache defaults in §17.1) and invalidated when the build files change. 5. **JDK stdlib** via `jrt-fs` on the running JDK — always appended unless `--no-jdk`, with `$JAVA_HOME/lib/src.zip` paired as its sources root, so `jdx members java.util.HashMap` works with zero configuration, with real JDK source and javadoc. **Maven coordinates** (`--coord g:a:v`, or a `g:a:v/` ref prefix) are resolved against `~/.gradle/caches` and `~/.m2` first; if absent, fetched from the configured repositories (`--repo` mirrors in flag order, Maven Central last) into `/m2/` (`~/.cache/jdx/m2/` on Linux; per-OS cache defaults in §17.1), **including the `-sources.jar`**, with checksum verification. This lets an agent evaluate a library the project does not yet depend on. Network fetching is opt-in per invocation via `--fetch` (or `fetch = true` in config) so the tool never surprises anyone with network traffic. --- ## 14. Interfaces: CLI, MCP, daemon, HTTP All four are thin adapters over `JdxService`. They share one command/flag metadata table, so adding a command makes it appear in all four automatically. ### 14.1 CLI (always available) Clikt-based. One-shot. Transparently uses the daemon if one is running (and `--no-daemon` forces in-process). This is the baseline every agent can use with zero setup. ### 14.2 MCP server — `jdx mcp` JSON-RPC over stdio, exposing each command as a typed MCP tool with a generated JSON schema. Why it matters: the agent gets structured arguments and tool descriptions instead of guessing shell syntax, cannot make quoting mistakes with `#` and `(`, and receives structured content back. For the stated goal — *an IDE for agents* — this is the highest-leverage interface. Tools exposed (v1): `jdx_show`, `jdx_members`, `jdx_signature`, `jdx_doc`, `jdx_body`, `jdx_source`, `jdx_outline`, `jdx_search`, `jdx_resolve`, `jdx_usages`, `jdx_hierarchy`, `jdx_callers`, `jdx_calls`, `jdx_samples`, `jdx_workspace`. The MCP server holds the index in memory for its lifetime — so it is effectively a per-session daemon, and repeated queries are instant. ### 14.3 Daemon — `jdx daemon` A background JVM holding a hot index (and, once touched, a warm Kotlin PSI environment), listening on a **unix domain socket** at `/jdx/.sock` (`$XDG_RUNTIME_DIR/jdx/…` on Linux; per-OS runtime dirs in §17.1 — a missing `XDG_RUNTIME_DIR` falls back per table, never `exit 3`). - The CLI auto-spawns it on first use and becomes a thin client: per-query latency drops from ~250 ms to ~10–20 ms. - **Idle shutdown after 5 minutes** of no requests (user-specified; `--idle ` to change, `0` to disable). Simple `ScheduledExecutorService` resetting on each request. - Version-stamped socket path, so an upgraded `jdx` never talks to a stale daemon. - `sun_path` guard: socket paths past 104 bytes on macOS / 108 elsewhere exit 3 naming the dir and the `JDX_RUNTIME_DIR` escape hatch (macOS `/var/folders/…` trips this) — never a generic bind failure or a silent cold fallback. - Start mutual exclusion via `-v1.lock` (`FileChannel.tryLock`, held while serving): a concurrent `start` fails fast naming the holder. - `stop` validates the pid file owns a `daemon run` child before signalling (never kills a reused pid), escalates `destroy()` to `destroyForcibly()`, and sweeps stale `.sock`/`.pid` files so a killed daemon restarts clean. - `jdx daemon status` reports uptime, workspace, memory, indexed artifacts, query count. ### 14.4 HTTP/JSON — `jdx serve` Built on the JDK's own `com.sun.net.httpserver` (zero dependencies), bound to `127.0.0.1` by default. `GET /v1/members?type=…&inherited=true`, `POST /v1/batch`, `GET /v1/health`, etc. Serves a different audience from MCP — non-MCP agents, editor plugins, scripts, notebooks, and remote use — and shares the exact JSON envelope of `--json`. Separate port/socket from the daemon; the two do not interfere. ### 14.5 The shared wire contract (v1) All four adapters speak one request format, so a payload is written and parsed in exactly one place: `core/.../rpc/RpcProtocol.kt` is normative, this section is the summary. A **request** is one line of JSON, newline-terminated (`\n`). Keys are in fixed order and `params` is sorted by key, so equal requests are byte-equal: ```json {"jdx":1,"command":"members","query":"com.google.gson.Gson","params":{"inherited":"true"}} ``` - `command` is one of the read-only queries — `show` `members` `outline` `body` `source` `signature` `doc` `search` `resolve` `ls` `tree` `usages` `hierarchy` `implementors` `callers` `calls` `samples` — plus `version`, `doctor` and `health`. The state-changing commands (`ws`, `cache`) are deliberately **not** on the wire in v1: one client of a shared daemon must not be able to reconfigure the others. - `params` values are text, the same spelling the CLI flags use. Typed clients (MCP schemas, HTTP query strings) may send JSON numbers and booleans; the decoder canonicalises them. - Decoding is generous where it is safe — surrounding whitespace, a trailing `\r\n`, absent `jdx`/`query`/`params`, and unknown keys (so a later version may add fields) are all accepted — and strict where a guess would be dangerous: an unknown command, a version it does not speak, a wrongly-typed field, a truncated line or trailing content are refused outright rather than half-understood. Decoding never throws, on any input. A **response** is not a new shape: it is the `--json` envelope of §8.2 verbatim, one per line. That is what makes the adapter-parity guarantee (`docs/TESTING.md` §9) structural rather than a per-adapter pile of goldens. --- ## 15. Performance plan |Path|Target|Technique| |---|---|---| |Cold CLI, simple query|≤ 250 ms|AppCDS archive; lazy module loading; no eager index scan| |Warm (daemon/MCP) query|≤ 20 ms|hot index, prepared statements, in-memory LRU| |First index of a 20k-class jar|≤ 8 s|virtual-thread fan-out, ASM `SKIP_FRAMES`, batched inserts| |Re-run on unchanged workspace|~0 ms|content-hash short-circuit| |`usages` over 20k classes|≤ 150 ms|indexed `ref.to_fqn` / `ref.to_member`| |Decompile a large class|≤ 500 ms first, ~0 cached|on-disk decompilation cache| Additional measures: - **Lazy source indexing** — never parse sources until a source-level query arrives. - **Lazy classloaders** for Vineflower and Kotlin PSI. - **`-XX:TieredStopAtLevel=1 -XX:+UseSerialGC -Xshare:auto -Xlog:cds=off`** in the launcher for the one-shot path (classic CLI JVM tuning; irrelevant and disabled for the daemon). `-Xlog:cds=off` keeps a stale AppCDS archive's mismatch notice off stderr, where it reads like a crash for a command that then works fine. - A `jdx bench` command, tagged out of normal test runs, measuring against the local `minecraft-client.jar` so regressions are caught. --- ## 16. Error handling, exit codes, degradation ### Exit codes `0` ok · `1` not found · `2` ambiguous · `3` usage error · `4` no index/workspace · `5` artifact read error · `6` internal error. **One documented exception: the `diff` gate.** `jdx diff` (§7.7) exits `0` whenever the comparison ran, whatever it found, and `--fail-on breaking|any` exits **`1`** when the gate trips — a deliberate, owner-approved reuse of `1`, since a diff has no "not found" outcome and the gate is a *result* rather than an error. The verdict is in the envelope too (`result.gate`), so nothing depends on reading the process status. No other command may bend the table. ### Degradation ladder Never fail where a lesser answer exists — but **always label the fallback**: ``` sources jar → decompiled (vineflower) → javap → signatures-only-from-bytecode → not found Kotlin metadata → JVM projection param names from source → MethodParameters → LocalVariableTable → arg0, arg1 javadoc on member → inherited javadoc from supertype → none ``` ### "Did you mean" On a miss, `jdx` suggests candidates by camel-hump match, then Levenshtein distance, then simple-name match in other packages — capped at 5, each printed as a canonical ref: ``` not found: com.google.gson.JsonParse did you mean: com.google.gson.JsonParser gson-2.14.0.jar com.google.gson.JsonParseException gson-2.14.0.jar ``` ### Warnings, never silent Duplicate FQNs across artifacts (shading!), sources/binary mismatch, corrupt class files, unsupported class-file major versions, multi-release jar variants, unresolvable supertypes (a hierarchy edge the workspace cannot provide — inherited members may be incomplete), project-discovery fallback (auto-discovery found the project but not its dependency set — dependency jars omitted, `PROJECT_DISCOVERY_FALLBACK`), unnameable class names (a class file whose own name carries an empty `$`-separated segment — JFR `Exception$JB$$Assertion` shapes — skipped with `UNNAMEABLE_CLASS`, never `CORRUPT_CLASS`), sealed/hidden classes — all surface as named warning codes in both text and JSON. --- ## 17. Security and safety - **Strictly read-only** with respect to user code. `jdx` writes only inside its own cache and config directories (per-OS defaults in §17.1). - **No code from an inspected jar is ever executed.** ASM parses; it does not load. Class initialisers never run. This is a meaningful property — agents inspect untrusted artifacts. - **Zip-slip / zip-bomb hardened**: entry-name normalisation, path traversal rejection, decompressed-size caps, entry-count caps. - **Network is opt-in** (`--fetch`), localhost-bound by default (`jdx serve`), with checksum verification on downloaded artifacts. - **Resource limits**: decompilation and source parsing run under a wall-clock timeout, so a pathological class cannot hang an agent's tool call. - **No telemetry.** ### 17.1 Platform directories (D-044, issue \#44) Phase-0 decision; Phase 1 implements it. Per-OS defaults for every location `jdx` writes. Supersedes D-013/D-027 and removes the `no XDG_RUNTIME_DIR → exit 3` contract: every OS has a defined fallback, so a missing `XDG_RUNTIME_DIR` (the norm on macOS/Windows) is never an error. |Root|Linux|macOS|Windows| |---|---|---|---| |cache|`~/.cache/jdx` (`$XDG_CACHE_HOME` when set)|`~/Library/Caches/jdx`|`%LOCALAPPDATA%/jdx/cache`| |config|`~/.config/jdx` (`$XDG_CONFIG_HOME` when set)|`~/Library/Application Support/jdx`|`%APPDATA%/jdx`| |daemon socket dir|`$XDG_RUNTIME_DIR/jdx`, else `$TMPDIR/jdx-$UID`, else `/run`|`$TMPDIR/jdx-$UID`, else `/run`|`%LOCALAPPDATA%/jdx/run` (AF\_UNIX, Win10 17063+ floor)| |install|`~/.local/share/jdx` + `~/.local/bin`|same as Linux|`%LOCALAPPDATA%/Programs/jdx` + PATH shim| `` / `` / `` below mean the resolved values of the cache / config / socket-dir rows. The daemon socket keeps its version-stamped name (`/jdx/-v1.sock`). **Override precedence** (first match wins; Phase 1 implements): 1. Explicit CLI flags (`--cache-dir` for the cache root, which covers the index DB, `m2/`, the Kotlin sidecar, and `auto/` workspaces). 2. `JDX_*` env (`JDX_CACHE_DIR`, `JDX_CONFIG_DIR`, `JDX_RUNTIME_DIR`). 3. Platform env: `XDG_CACHE_HOME` / `XDG_CONFIG_HOME` / `XDG_RUNTIME_DIR` on Linux, `TMPDIR` for the macOS socket dir, `LOCALAPPDATA` / `APPDATA` on Windows. 4. OS default from the table above (with the socket-dir `else` fallbacks). **Migration:** on first run under the new defaults, if the OS-default location is absent/empty and the old location (`~/.cache/jdx`, `~/.config/jdx`) exists with content, move it (rename, never copy-merge) to the new location and print an info line; if both exist, keep the new location and warn naming the old one. Linux defaults are unchanged, so Linux installs never migrate. ### 17.2 Release assets and signing (issue \#48) Tagged pushes (`vX.Y.Z` on `main`) build in `.github/workflows/release.yml` on a three-OS matrix (`ubuntu` / `windows` / `macos` — the Windows leg builds through `gradlew.bat`, and each leg smoke-tests its own launcher: `jdx.bat` on Windows, `./app/build/jdx` elsewhere). Each leg publishes two bundles plus checksums: |Asset|Contents| |---|---| |`jdx--.tar.gz` (+ `.sha256`)|`jdx/` + `jdx/libs/` layout: `jdx`, `jdx.bat`, `jdx.ps1`, `libs/jdx-*-all.jar`, `libs/jdx.jsa`| |`jdx--.zip` (+ `.sha256`)|Same membership (Explorer/Defender-first for Windows)| `` is `linux`, `macos`, or `windows`. The `linux` leg additionally publishes bare `jdx-.tar.gz/.zip` aliases (byte copies): that is the URL `install-release.sh` and `jdx upgrade` fetch by default, so old clients keep working. Checksums are ` ` lines verified with `sha256sum`, `shasum -a 256`, or Windows `CertUtil` — a published checksum that cannot be checked fails the install loudly, never silently. `install-release.sh` `--tarball` accepts either extension (re-applying `+x` to the POSIX launcher after unzip, since repacked zips may lose it). The `jdx.jsa` AppCDS archive in each bundle is trained by that leg's own JDK: the archive format is host-specific and a foreign one is ignored via `-Xshare:auto`, so per-OS training is what makes the ≤ 250 ms cold-start budget (§15) hold on every OS instead of only Linux. Build skew (issue #54, closed): the archive is keyed to the exact JDK *build* (CI Temurin 21), so a different vendor, update, or newer JDK (e.g. a local 27) rejects it — but every launcher passes `-Xshare:auto -Xlog:cds=off` with `-XX:SharedArchiveFile` after `-Xshare:auto`, so the rejection degrades silently to the plain run: exit 0, correct output, empty stderr, just without the archive speedup. Silencing matters beyond cosmetics: the `[warning][cds]` notice lands on *stdout*, where it would corrupt piped and `--json` output. The silent-degradation path is pinned end-to-end by `LauncherScriptTest` (a garbage archive still yields a clean `jdx version`). `jdx upgrade` keeps tracking the bare `.tar.gz` (portable layout, pure-Java extraction, all launchers inside — it upgrades Windows installs without any external `tar`). **Signing: releases are unsigned** (no SmartScreen / Gatekeeper notarization). First-run warnings are expected: `Unblock-File` (PowerShell) on Windows, `xattr -d com.apple.quarantine /jdx` on macOS. Adding signing is tracked in #31 (packaging), not here. --- ## 18. Testing strategy > **Full strategy: [`docs/TESTING.md`](https://mohammadmd1383.github.io/jdx/docs/testing/).** Summarised here; that document is > authoritative and is required reading before writing tests. This is a project whose worst bugs are *"the real world contains a jar shaped like **that**"* bugs — not logic errors in code we wrote. Tests written from imagination cannot find those. So TDD is the floor, not the ceiling: it is **mandatory for `core`** (which is dependency-free and has no excuse for being hard to test), and it is paired with five families of test that **generate their own cases** instead of relying on anyone to enumerate them. |Family|What it catches| |---|---| |**Unit (TDD)**|logic errors in rules we defined — `core`, test-first, no exceptions| |**Property-based**|edge cases nobody imagined (ref round-trips, descriptor parsing, resolver invariants, truncation)| |**Differential / oracle**|wrong bytecode interpretation — member sets and descriptors diffed against `javap -p -s` for every fixture and a sampled slice of the real corpus| |**Golden / approval**|accidental output changes in every renderer, text and JSON| |**Metamorphic**|inconsistency between related answers (`hierarchy --up` ⟺ `hierarchy --down`, `callers` ⟺ `calls`, `--inherited` ⊇ `--declared`, run-twice determinism)| |**Fault injection**|crashes on malformed input — truncated classes, zip-slip, zip bombs, mismatched sources jars, `-g:none`, decompiler timeouts| |**Corpus soak**|real-world shapes we never imagined — every command over the ~2,183 local jars, asserting *invariants* (never crash, always valid JSON, always deterministic) rather than values| |**Parity / contract**|front-end drift — CLI `--json`, HTTP and MCP must return byte-identical payloads| |**Performance regression**|silent slowdowns against the §15 budgets| |**Mutation (Pitest)**|tests that run code without asserting anything — `core` gated at ≥ 80 % mutation score| Two oracles do most of the work and cost almost nothing: **`javap`**, which tells us the ground truth for any class file without us writing it down, and **compile-back testing** for decompilation (decompiled source must recompile to the same API surface — a far more useful assertion than pinning decompiler text that changes every release). Tests run in four tiers so the inner loop stays fast: `test` (\< 30 s, the TDD loop), `check` (\< 3 min, pre-commit), `soak` (corpus, tagged off by default), and `bench`/`mutationTest` (on demand). A tier-1 suite that creeps past 30 s kills TDD, and then it kills testing — that budget is enforced. ## 19. Dependencies and licensing |Dependency|Purpose|License| |---|---|---| |Kotlin stdlib|language|Apache-2.0| |ASM|bytecode reading|BSD-3-Clause| |kotlin-metadata-jvm|`@Metadata` decoding|Apache-2.0| |JavaParser|`.java` parsing|Apache-2.0 (dual w/ LGPL — we take Apache-2.0)| |kotlin-compiler-embeddable|`.kt` PSI parsing (lazy, side-loaded)|Apache-2.0| |Vineflower|decompilation|Apache-2.0| |sqlite-jdbc (Xerial)|index storage|Apache-2.0| |Clikt|CLI parsing|Apache-2.0| |kotlinx-serialization-json|JSON envelope|Apache-2.0| |MCP Kotlin SDK|MCP transport *(fallback: ~300 LoC hand-rolled JSON-RPC)*|MIT| |JDK `com.sun.net.httpserver`|HTTP server|— (JDK builtin)| |JUnit 5 + kotlin-test|tests|EPL-2.0 / Apache-2.0| All permissive; all compatible with shipping `jdx` itself under **Apache-2.0**. Bundled third-party notices go in `NOTICE`. --- ## 20. Delivery plan / milestones Each milestone ends with a working, committed, demoable binary. |#|Milestone|Contents|Demo gate| |---|---|---|---| |**M0**|Skeleton|Gradle multi-project, version catalog, fat jar, `jdx` launcher, `jdx version`, `jdx doctor`, CI-less local test task|`./jdx doctor` prints a clean environment report| |**M1**|Read path|Artifact loading + sources pairing, ASM class model, symbol-ref parser/printer, `show`, `outline`, `members --inherited` with generic substitution, text + JSON renderers, JDK jrt root|`jdx members java.util.HashMap --inherited` matches IntelliJ's completion list| |**M2**|Index|SQLite store, content-hash keying, parallel indexer, `search` (glob/regex/camel-hump), `ls`/`tree`, workspaces, project auto-discovery, `cache` cmds|gson + JDK indexed; `jdx search 'H*Map'` instant| |**M3**|Bodies|Sources jars, JavaParser, srcmap, `body`, `source`, `signature`, `doc` (incl. inherited doc), Vineflower engine + cache, `javap` engine|`jdx body 'com.google.gson.Gson#toJson(Object)'` from sources **and** from a sources-less jar| |**M4**|Graph|Reference-edge extraction, `usages`, `hierarchy`, `implementors`, `callers`, `calls --depth`, `samples`, project source-dir usages|`jdx usages` on a Minecraft symbol under 150 ms| |**M5**|Kotlin|`@Metadata` rendering, Kotlin member mapping, `--view jvm`, side-loaded Kotlin PSI source parsing|`jdx members` on a coroutines class shows `suspend fun` and nullability| |**M6**|Serving|Daemon + unix socket + 5-min idle shutdown, transparent CLI client, MCP server, HTTP server, `batch`|Same query answered identically via CLI, MCP, and HTTP| |**M7**|Polish|Token budgets, did-you-mean, warnings, AppCDS, `help --agent`, README, install script, `bench`|Cold query ≤ 250 ms; docs complete| **Phase-2 backlog** is §21. GitHub Issues track limitations and the backlog against this table. --- ## 21. Deferred to phase 2 Explicitly out of v1 scope, recorded so they are not lost: - ~~**`jdx diff a.jar b.jar`** — public-API diff with binary-compatibility warnings, for dependency upgrades.~~ **Shipped** (issue #23): spec in §7.7, flags in Appendix B. - **Mappings / remapping** — Minecraft-style obfuscated jars: read Tiny/SRG/ProGuard mapping files so `jdx` can answer in either namespace. High value on this particular machine. - **Resources \& metadata inspection** — `META-INF/services`, `module-info`, manifest attributes, SPI providers, resource listing and extraction. - **Annotation-driven views** — "show me every `@Deprecated(forRemoval=true)` in this dependency", "all Spring `@Bean` factories", "all JUnit `@Test`s". - **`--since` / API-level reporting** — which JDK release introduced a member. - **Dataflow-lite** — `jdx flow ` for a simplified reaching-values view. - **Multi-release jar variant selection** beyond warning. - **Scala/Groovy language views.** - **Publishing** — Homebrew/AUR packaging, GitHub Releases with a native-image build. --- ## 22. Risks |Risk|Impact|Mitigation| |---|---|---| |`kotlin-compiler-embeddable` bloat/startup (~55 MB, ~1 s)|Slow cold path, big install|Side-loaded jar, isolated lazy classloader, warm in daemon, `doctor` reports it. Fallback lexer extractor kept behind a flag.| |Decompiler output is wrong or unreadable on hard methods|Agent reasons from bad code|Always labelled as reconstructed; `--engine javap` escape hatch; phase-2 second engine| |Index staleness on rapidly-changing project dirs|Wrong answers|Content hashing for jars, per-file mtime for dirs, `--force`, hash in `doctor`| |Global SQLite DB grows unbounded|Disk usage|`cache gc`, per-artifact eviction by last-use, `cache info`| |Shaded/duplicate FQNs across a 2000-jar classpath|Silently wrong class shown|Classpath-order shadowing + explicit duplicate warning with all providers listed| |Scope creep (this document is large)|Never ships|Strict milestone gating; M1–M3 alone already deliver the headline value| |Output format churn breaking agent prompts|Agents mis-parse|`jdx: 1` envelope version; golden tests; changes to text layout treated as breaking| --- ## Appendix A: worked agent session The scenario from the original brief — *"I want the body of `org.some.lib.AClass.someMethod(int,int)`"* — plus the investigation around it. ```console $ jdx ws create app --auto # discovers the Gradle project + its cached jars + JDK workspace 'app' created · 214 artifacts · 18,443 classes · indexed in 4.1s $ jdx search 'JsonAdapter' --kind class class com.google.gson.annotations.JsonAdapter gson-2.14.0.jar annotation class com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory gson-2.14.0.jar 2 results $ jdx members com.google.gson.Gson --inherited --grep 'json' methods on com.google.gson.Gson public T fromJson(String, Class) throws JsonSyntaxException public T fromJson(String, Type) throws JsonSyntaxException public T fromJson(JsonElement, Class) throws JsonSyntaxException public String toJson(Object) public String toJson(Object, Type) public void toJson(Object, Appendable) throws JsonIOException public JsonElement toJsonTree(Object) + 9 inherited from java.lang.Object (--from java.lang.Object) 7 of 39 methods shown (filtered by --grep 'json') $ jdx body 'com.google.gson.Gson#toJson(Object)' com.google.gson.Gson#toJson(java.lang.Object) source: gson-2.14.0-sources.jar · com/google/gson/Gson.java:722-728 public String toJson(Object src) { if (src == null) { return toJson(JsonNull.INSTANCE); } return toJson(src, src.getClass()); } next: jdx body 'com.google.gson.Gson#toJson(java.lang.Object, java.lang.reflect.Type)' $ jdx usages 'com.google.gson.Gson#toJson(Object)' --limit 5 5 of 23 usages app/src/main/java (2) com.example.api.UserController#serialize(User) UserController.java:44 call com.example.api.ErrorMapper#toBody(Throwable) ErrorMapper.java:19 call gson-2.14.0.jar (3) com.google.gson.Gson#toJson(Object, Appendable) call com.google.gson.internal.Streams#write(JsonElement, …) call … $ jdx hierarchy com.google.gson.TypeAdapter --down --in 'app/**' com.google.gson.TypeAdapter ↓ subclasses in app/** com.example.json.InstantAdapter extends TypeAdapter com.example.json.MoneyAdapter extends TypeAdapter $ jdx samples 'com.google.gson.TypeAdapter#write' --limit 1 example from app/src/main/java · com.example.json.InstantAdapter#write:21-24 @Override public void write(JsonWriter out, Instant value) throws IOException { if (value == null) { out.nullValue(); return; } out.value(DateTimeFormatter.ISO_INSTANT.format(value)); } $ jdx body 'net.minecraft.world.item.ItemStack#getMaxStackSize()' # no sources jar net.minecraft.world.item.ItemStack#getMaxStackSize() source: decompiled by vineflower 1.11 from minecraft-client.jar ⚠ reconstructed public int getMaxStackSize() { return this.getOrDefault(DataComponents.MAX_STACK_SIZE, 1); } next: jdx body '…#getMaxStackSize()' --engine javap (exact bytecode) ``` Seven questions answered, no file dumped, nothing grepped, total output a few hundred tokens. The equivalent investigation with `unzip`/`javap`/`grep` is roughly 20 commands and tens of thousands of tokens. --- ## Appendix B: full flag reference *(Maintained as commands land; `jdx help --agent` is generated from the same metadata and is the authoritative machine-readable version.)* ### Global ``` -w, --workspace use a named workspace --jars add binary roots (repeatable) --src add source roots (repeatable) --coord add a Maven coordinate root (repeatable) --repo add a Maven repository base URL for --coord fetches (repeatable) --sources explicitly pair a sources jar --jdk / --no-jdk include the JDK stdlib (default: include) --fetch allow network fetches from Maven repositories (Central last by default) --json structured output --no-color disable ANSI even on a TTY --max-lines bound output size --brief minimal output (signatures only, no doc) -v, --verbose / -q, --quiet --no-daemon force in-process execution --timeout per-operation wall-clock cap --cache-dir override the OS cache default (§17.1; ~/.cache/jdx on Linux) ``` ### Per-command highlights ``` show [-w …] [--jars …] [--no-jdk] [--json] [--no-color] outline [--kind --static/--instance --access --from --grep --include-synthetic --limit --brief --max-lines --view kotlin|jvm] [-w …] [--jars …] [--no-jdk] [--json] [--no-color] members --inherited/--declared --kind --static --instance --access --from --grep --with-doc --brief --max-lines --sort --view kotlin|jvm --include-synthetic [-w …] [--jars …] [--no-jdk] [--json] [--no-color] body --engine vineflower|javap --with-doc --with-signature --context N --line-numbers --max-lines source --lines A:B --around --context N --line-numbers --max-lines --engine vineflower|javap signature [--include-synthetic --limit --view kotlin|jvm] doc --inherited/--no-inherited --raw --max-lines search --kind --regex --fuzzy --in --package --limit resolve [--limit] ls [package-glob] [--limit] tree [artifact-glob] [--depth --counts --limit] usages --kind call|ref|impl|override|read|write|new|throw|annotation|all --in --exclude --limit --context --src [-w …] [--jars …] [--no-jdk] [--json] [--no-color] hierarchy --up --down --direct --depth N --in --exclude --limit [-w …] [--jars …] [--no-jdk] [--json] [--no-color] implementors [--direct --depth N --in --exclude --limit] [-w …] [--jars …] [--no-jdk] [--json] [--no-color] callers --depth N --in --exclude --limit [-w …] [--jars …] [--no-jdk] [--json] [--no-color] calls --depth N --in --exclude --external-only --limit [-w …] [--jars …] [--no-jdk] [--json] [--no-color] samples --limit N --prefer-sources [-w …] [--jars …] [--no-jdk] [--json] [--no-color] diff [--visibility public|all] [--include-synthetic] [--severity all|suspicious|breaking] [--fail-on none|breaking|any] [--limit N] [--fetch] [--repo] [--json] [--no-color] (no --jars/-w/--no-jdk/--no-daemon: a diff names its own two artifacts) ws create|list|info|remove|add|use --auto --jars --src --coord --repo --jdk index --force -w cache info|gc|clear [--cache-dir --json] (gc: --dry-run) kotlin install [--repo --force] [--json] daemon start|stop|status|restart --idle serve [-w …] --port --bind mcp [-w …] (stdio; per-call workspace override) batch --json bench [--jars …] [-w …] [--no-jdk] [--iterations N] [--json] upgrade [--version ] [--check] [--repo OWNER/NAME] [--json] setup --agent opencode|claude-code|cursor|kilo|cline|codex|copilot|antigravity --scope project|system [--check] [--remove] [--json] help [--agent] [--json] ``` --- # Testing strategy > **Short version:** TDD is mandatory for `core`, but TDD alone would leave this project > badly under-tested. `jdx` reads files written by other people's compilers; its worst bugs > are not logic errors in code we wrote, they are *"the real world contains a jar shaped like > **that**"* errors. Tests written from imagination cannot find those. So we pair TDD with > five families of test that generate their own inputs or borrow their expected answers from > an external oracle. --- ## 1. Why not just TDD? TDD's loop is: *write a failing test for the behaviour you want → make it pass → refactor.* That works beautifully when **you** define correct behaviour. Roughly 40 % of this project is like that — the symbol-ref grammar, member resolution, generic substitution, truncation, rendering. For all of it, TDD is required (§3). The other 60 % is different in kind: |Area|Why TDD alone is weak|What we use instead| |---|---|---| |ASM class reading|Correct output is "whatever `javac`/`kotlinc` actually emitted" — you cannot invent it|**Differential testing vs `javap`** (§5)| |Decompilation|Output is whatever Vineflower produces; asserting exact text is brittle|**Property + compile-back testing** (§5.3)| |Jar/zip handling|Bugs come from malformed input nobody thought to write a test for|**Fault injection + corpus soak** (§7, §8)| |Index \& queries|Bugs appear at 20,000 classes, not at 3|**Metamorphic + corpus testing** (§6, §8)| |Four front-ends|Drift between them is invisible to per-front-end tests|**Parity testing** (§9)| |"Do my tests have teeth?"|Line coverage says yes while asserting nothing|**Mutation testing** (§10)| The strategy below is the answer to *"more tests guarantee fewer bugs"* — not more hand-written examples, which plateau fast, but **test families that keep generating new cases without anyone writing them.** --- ## 2. The ten families at a glance |#|Family|Catches|Where|Tier| |---|---|---|---|---| |1|**Unit (TDD)**|logic errors in our own rules|`core` (mandatory), all modules|1| |2|**Property-based**|edge cases nobody imagined|parsers, resolvers, truncation|1| |3|**Differential / oracle**|wrong bytecode interpretation|`index` ASM reader|2| |4|**Golden / approval**|accidental output changes|all renderers, all commands|1| |5|**Metamorphic**|inconsistency between related answers|resolver, index, graph queries|2| |6|**Fault injection**|crashes on malformed input|artifacts, sources, decompiler|2| |7|**Corpus soak**|real-world shapes we never imagined|everything, over ~2,183 local jars|3| |8|**Parity / contract**|front-end drift, text↔JSON gaps|`cli`/`mcp`/`server`|2| |9|**Performance regression**|silent slowdowns|indexer, queries|4| |10|**Mutation**|tests that assert nothing|`core` gated, others measured|4| **Tiers** (so the fast loop stays fast): |Tier|Command|Budget|When| |---|---|---|---| |1|`./gradlew test`|**\< 30 s**|every save; the TDD loop| |2|`./gradlew check`|**\< 3 min**|before every commit| |3|`./gradlew soak`|minutes|before finishing a task; needs local jars| |4|`./gradlew bench mutationTest -PconfirmTier4=true`|long|on demand / before a milestone closes| A tier-1 suite that creeps past 30 s kills TDD, and then it kills testing. Guard it: anything touching disk, network, or a real jar belongs in tier 2 or above. **Tags are the mechanism (T-053).** JUnit `@Tag`s decide which tier runs a test class: |Tag|Runs in|Meaning| |---|---|---| |*(none)*|`test` (tier 1, alias `unitTest`)|fast unit tests only — no disk, no subprocesses, no real jars| |`integration`, `tier2`|`integrationTest` (tier 2, alias `tier2Test`), via `check`|slow suites: jar reads, JVM spawns, golden files, fault injection, parity| |`soak`|`soakTest`, via `soak` (tier 3)|corpus soak over real jars; never runs in `check`| |`bench`|`benchTest`, via `bench` (tier 4)|benchmarks| The wiring lives in the root `build.gradle.kts`: per-module `integrationTest`/`tier2Test`/`soakTest`/ `benchTest` tasks with `includeTags`, `test` excluding all four tags (`integration`, `tier2`, `soak`, `bench`), `check` depending on `integrationTest`, and root `soak`/`bench`/`mutationTest`/`testReport` commands. `SoakExclusionProofTest` (`core/.../tiers/`) is the deliberate proof that a `soak`-tagged test cannot run in tier 1 or 2: it asserts `-Djdx.tier=soak`, a property only the `soakTest` task sets, so broken exclusion fails the build instead of silently slowing the loop. Soak tests read the corpus directory from `-Djdx.corpusDir` (default `~/.gradle/caches`, override with `-Pcorpus=`) and must skip — never fail — when it is absent, so `check` stays green on machines with no corpus. **The per-test budget is enforced, not aspirational.** Every `test` and `integrationTest` task is finalised by `verifyPerTestBudget`, which inspects JUnit XML times, prints total test time, the 10 slowest tests, and any budget violations at the end of the run. Unit tests (`test`) must finish in \<= 2.5 s (override with `-Punit.test.budget=`), while integration tests (`integrationTest`) must finish in \<= 120.0 s (override with `-Pintegration.test.budget=`). When a unit test exceeds its budget, the fix is to optimize it or move it to integration with `@Tag("integration")`. **CI mode (`-Pci`, issues #57/#61).** CI runners are slow, shared, and variable — the same suite passes locally and reds on CI with 0 test failures (issue #2). CI workflows pass `-Pci` (also auto-detected via `CI`/`GITHUB_ACTIONS` env), which relaxes *only* wall-clock test gates: `verifyPerTestBudget` becomes report-only (a slow runner with 0 failures stays green) and PIT `timeoutConstInMillis` lifts from 10 s to 60 s in every gated module. No per-task `Test` timeout is set in either mode (fail-open by policy). Real product timeouts (Vineflower/javap 30 s, Maven connect/read) are degradation behavior and stay intact — never weaken product behavior to green CI. A local `verifyPerTestBudget` red (issue #2) is a machine-variance signal to move a suite to tier 2; a CI-infra red is a runner problem — do not confuse the two. --- ## 3. TDD — where it is mandatory **`core` is developed test-first. No exceptions.** `core` has no dependencies and no IO (D-001, CONTRIBUTING.md), which means there is never an excuse for "hard to test". The loop, concretely, for T-003 (symbol-ref parser): ```kotlin // RED — write this first, watch it fail for the right reason @Test fun `nested type accepts dollar and dot forms identically`() { assertEquals(parseRef("java.util.Map\$Entry"), parseRef("java.util.Map.Entry")) } // GREEN — the smallest change that passes. Resist generalising here. // REFACTOR — now make it clean, with the test holding the behaviour still. ``` Rules that make this work rather than become ceremony: - **One behaviour per test.** If the name needs "and", it is two tests. - **Test names are sentences**, in backticks: ``fun `inherited members from a generic supertype are substituted`()``. The test report should read like a specification of what `jdx` does. - **Watch the test fail first**, and check the failure message is the one you expect. A test that was green before you wrote the code is testing nothing. - **Assert on values, not on calls.** No mocking framework in `core`. If you need a mock, the design is wrong — push the IO outward. - **Every bug fix starts with a failing regression test** that reproduces the report. Name it after the issue: ``fun `regression #42 - bridge method shadows its own override`()``. **Outside `core`,** test-first is encouraged but not required, because you often cannot know the expected value until you have looked at what ASM or Vineflower actually returns. There the honest workflow is: spike → look → **write the differential or golden test that pins it** → delete the spike. What is *not* acceptable is landing untested code and promising tests later. --- ## 4. Property-based testing (§family 2) Hand-written examples stop finding bugs once they cover what you already thought of. Properties keep generating inputs forever. Library: **kotest-property**. Minimum 1,000 cases per property in tier 1 (`JDX_PROPERTY_ITERATIONS` in `core/src/test/kotlin/dev/jdx/core/gen/PropertySupport.kt`). When a property fails, kotest prints the failing seed (`Repeat this test by using seed …`); **pin it as a regression test** with one line — `checkAll(1_000, pinnedConfig(L), arbFoo()) { foo -> … }` — and remove the pin once the bug is fixed and the property passes unpinned again. Properties we owe the project: ```kotlin // Symbol refs (T-003) "parse(print(ref)) == ref" // round-trip is a fixed point "print(parse(s)) is stable" // idempotent normalisation "a malformed ref never throws" // returns a positioned error // Descriptors and signatures (T-002, generators + properties T-055) "descriptor -> parsed -> printed == descriptor" // for generated nested generics, // wildcards, arrays, type vars "binaryName -> fromBinaryName -> binaryName == identity" // TypeNamePropertyTest "parseClass(classSignature).signature == signature" // the T-009 class-file entry point // Member resolution (T-009) "resolve(type) is deterministic across runs" "resolve(type) contains every member of declared(type)" "resolve(type) never contains a private member of a strict supertype" "resolve of a cyclic hierarchy terminates" // synthesised cyclic ClassInfo // Truncation (T-047) "truncated output never splits an entity" "shown <= total, and hint is present iff shown < total" ``` Generators live in `core/src/test/kotlin/.../gen/` and are shared. Writing a good `Arb` once pays for itself across every later property. --- ## 5. Differential / oracle testing (§family 3) **This is the single most valuable family in the project**, because it gives us correct answers we did not have to write down. ### 5.1 `javap` as the oracle for bytecode reading For **every** class in the fixture corpus *and* every class in a sampled slice of the local jar corpus: ``` javap -p -s -c ──► parse ──► { member names + descriptors + flags } jdx members --declared --access all --include-synthetic --json ──► same set assert the two sets are EQUAL ``` Any disagreement is a bug in `jdx` (or, rarely, a `javap` quirk worth documenting). This one test family covers a huge surface — access flags, synthetics, bridges, varargs, nested classes, enum internals, records — **without anyone enumerating those cases.** ### 5.2 `javac`/`kotlinc` as the oracle for source-derived facts The fixture corpus is compiled from source we control, so for every fixture we independently *know* the truth. Assert `jdx`'s answer against a machine-readable expectation file generated from the fixture's own annotations: ```java @ExpectedMembers({"public int add(int,int)", "private static final long serialVersionUID"}) public class Arithmetic { … } ``` The expectation travels with the fixture, so adding a fixture adds a test automatically. ### 5.3 Compile-back testing for decompilation Do **not** assert decompiler output text — it changes with every Vineflower release and the test becomes noise. Assert the property that actually matters: ``` decompile(class) ──► javac ──► class' (must compile) members(class) == members(class') (same API surface) ``` A decompilation that recompiles to the same API is a decompilation an agent can trust. When it fails, that is real information (`⚠ reconstructed` already warns the user; this test tells *us* how often it matters). ### 5.4 The JDK as a free oracle `java.base` ships ~6,000 classes plus `src.zip`. It is a large, stable, sources-paired corpus that every contributor has. Use it: `jdx members java.util.HashMap --inherited` compared against the JDK's own source declarations is a strong end-to-end check that needs no downloads. --- ## 6. Metamorphic testing (§family 5) When you cannot state the right answer, you can often state a **relation between answers** that must hold. These catch deep index and resolver bugs that example tests never reach. ``` members(T, --inherited) ⊇ members(T, --declared) members(T, --inherited) ⊇ members(super(T), --inherited) minus private/overridden hierarchy(T, --down) contains S ⟺ hierarchy(S, --up) contains T usages(X) — every hit, when re-read, genuinely references X callers(M) contains C ⟺ calls(C) contains M search(exact-fqn-of(T)) returns exactly T show(T) member counts == |members(T, --declared, --access all)| body(M) parsed back has signature == signature(M) index(jar) twice ⟹ byte-identical index rows (determinism) run(cmd) twice ⟹ byte-identical stdout (D-007 determinism promise) ``` Run these over the fixture corpus in tier 2, and over a random sample of the local jar corpus in tier 3. They are cheap to write and they are the tests most likely to find a real bug six months from now. --- ## 7. Fault injection (§family 6) `jdx`'s promise is *degrade, don't fail* (AGENTS.md). That promise needs adversarial tests, generated programmatically rather than collected by hand: - Truncated class file (cut at every 10 % boundary) - Class-file major version from the future - Zip entry with `../` traversal (**zip-slip**) — must be rejected, D-017 - Zip bomb: huge declared uncompressed size — must hit the cap, not OOM - Empty jar; jar with only resources; jar with a directory named `X.class` - Sources jar that does not match its binary (member added, removed, renamed) — must emit `SOURCES_VERSION_MISMATCH`, not silently disagree - Sources jar for a *different* artifact entirely - Class compiled `-g:none` (no debug info ⟹ no parameter names) — must fall back, and **say** the names are synthesised - Multi-release jar with conflicting versions - Duplicate FQN across two jars (shading) — must warn, D-016 - Unreadable file / permission denied / file deleted mid-read - Decompiler timeout and decompiler crash — must fall back to `javap`, not propagate **Every one of these asserts two things:** the process exits with a documented code (D-015), and the output names the problem. A stack trace reaching an agent is a test failure. --- ## 8. Corpus soak testing (§family 7) The highest-value-per-line test in the project, and the one most teams skip. The owner's machine has **~2,183 real jars** in `~/.gradle/caches`, including sources jars and a large obfuscated `minecraft-client.jar`. That is a free adversarial corpus written by dozens of different compilers and build tools. ``` for each jar in the corpus: index it for a random sample of classes in it: run show / outline / members / signature / doc / body / source assert: - exit code ∈ {0,1,2} (never 5 or 6) - no exception reached stdout/stderr - --json output parses and validates against the envelope schema - text and JSON carry the same entity set (D-007) - running it twice produces identical bytes (determinism) record every warning code emitted, with a count ``` Notes: - **Tagged, off by default** (`./gradlew soak`) — it depends on artifacts not every contributor has, and it takes minutes. `./gradlew check` must never require it. - **It asserts invariants, not values.** It cannot know the right answer for 2,183 jars; it can know that `jdx` must never crash, never emit invalid JSON, and never be non-deterministic. - **The warning histogram is a deliverable.** "247 jars emitted `MULTI_RELEASE_VARIANT`" is how we learn which real-world shapes matter. Record it in the progress log. - Point it at a directory via `-Pcorpus=` so contributors without the owner's cache can run it against their own `~/.m2` or `~/.gradle`. --- ## 9. Parity / contract testing (§family 8) Four front-ends (D-004) will drift unless a test forbids it. - **Adapter parity:** the same query through CLI `--json`, HTTP, and MCP must produce **byte-identical result payloads**. One test, parameterised over every command. - **Text↔JSON completeness:** every entity and warning in the text rendering must appear in the JSON. Enforced structurally, not by eyeball (D-007). - **Exit-code contract:** a table-driven test asserting the D-015 code for each documented situation. Changing a code must break a test — that is the point. - **`--help` coverage:** every flag documented in `docs/PROPOSAL.md` Appendix B exists, and every implemented flag is documented. Drift in either direction fails. - **Envelope schema:** the JSON envelope is validated against a checked-in JSON Schema, so `"jdx": 1` genuinely means something. --- ## 10. Mutation testing (§family 10) The direct answer to *"don't stand with a few test cases."* Line coverage measures which code ran; **mutation score measures whether the tests would notice if that code were wrong.** Pitest mutates the bytecode (flips conditionals, removes calls, changes return values) and reports how many mutants your tests killed. Targets: |Module|Line coverage|Mutation score| |---|---|---| |`core`|≥ 95 %|**≥ 80 %** (gate)| |`index`, `sources`, `decompile`|≥ 85 %|≥ 65 % (measured, reported)| |`cli`, `mcp`, `server`|no gate — thin by rule, covered by parity + golden tests|—| Run in tier 4. A surviving mutant in `core` is a genuine gap: either write the test that kills it, or delete the unreachable code. Runbook: `./gradlew mutationTest -PconfirmTier4=true` (or `-PallowHeavy=true`) runs PIT over `core`, `index`, `sources` and `decompile` (only `core`'s ≥ 80 % gate fails the build; the rest are measured and reported per D-021). Reports land in `/build/reports/pitest` (XML + HTML, un-timestamped); JaCoCo line reports sit beside them under `build/reports/jacoco/test/`. The full run takes ~30 min (the `index` suite dominates) and never runs in `check`. Explicit permission is required via `-PconfirmTier4=true` or `-PallowHeavy=true`. For fast iteration on one surviving mutant, `./gradlew :core:pitest -PpitestScope='dev.jdx.core.render.Truncation*'` narrows both the class and test filters to that glob — never quote a scoped run as the module's score (L-056). Three PIT/Kotlin wrinkles are handled in the build files, not worked around per-case: test discovery goes through `pitest-junit5-plugin` (JUnit-5-only upstream, verified working on this build's JUnit 6); `kotlin.jvm.internal.Intrinsics` calls are excluded via `avoidCallsTo` (equivalent mutants by construction); and build-wiring proof tests (`core.tiers`, the index soak suites) are excluded from PIT targets — they assert runner invariants, not product behaviour. ### 10.1 A mutation gate that cannot run is not a gate PIT runs its suite once **unmutated** first, to compute line coverage, and refuses to start if that run is red: *"N tests did not pass without mutation … Mutation testing requires a green suite."* Nothing is mutated, no report is written, and the module's score is simply never produced. On a contributor's machine that reads as "a slow run"; in CI it reads as a red nightly, which is easy to mistake for a mutation problem. Two environment facts make that pre-scan fail on a **clean checkout only** (issue #66, the reason it survived for weeks): 1. A PIT minion is a fresh JVM. It inherits no `test`/`tier2Test` system property, so the corpus property `jdx.fixturesDir` never reaches it and the resolver falls back to walking up for `testfixtures/build/libs` — which a developer's earlier build has left behind and a runner has not. 2. `dependsOn(":testfixtures:jar", …)` was on `test`/`tier2Test` only, and `./gradlew mutationTest` reaches neither. So the corpus wiring is **shared, in the root build, for every module that applies the PIT plugin** — one place, not a private copy in `index`: - the four `:testfixtures` jar tasks are dependencies of the `pitest` task; - `jdx.fixturesDir` and `jdx.diffFixturesDir` go on `pitest { jvmArgs }` (the *minion* arguments — `PitestTask`'s own `JavaExec` args are the launcher's and would not help). `./gradlew verifyPitestWiring` asserts both halves for every PIT module and runs in every `check`, so a module added tomorrow inherits the wiring and a regression is red on a push rather than in a 20-minute weekly run. It also asserts the PIT modules are exactly the ones `mutationTest` runs, and that it checked something at all. **A missing corpus is a machine fact, not a defect.** The shared resolvers expose `binaryCorpusAvailable()` / `sourcesCorpusAvailable()` (and `DiffFixtureJars.available()`), and the suites that need the corpus `assumeTrue` on them. Resolving optimistically and letting the resolver throw is what turned one missing environment detail into a red build whose stack trace pointed at product code. `FixtureResolutionOwnershipTest` in `:testfixtures` pins both halves: exactly one copy of the walk-up resolver may exist (plus core's documented second copy), and the suites rewritten in #66 must stay gated. --- ## 11. The fixture corpus (T-006) Every family above leans on this, so it is built early and deliberately. Small, nasty Java and Kotlin classes compiled by Gradle at test time into **both** a binary jar and a sources jar. Must include: bounded/wildcard/recursive generics · bridge methods from covariant overrides · inner, static-nested, anonymous and local classes · records · sealed hierarchies · enums with constant bodies · repeatable and array-valued annotations · varargs · `synchronized`/`native` /`strictfp` · `@Deprecated(forRemoval=true)` · package-private and private members · a class compiled `-g:none` · a class with a static initialiser that writes a marker file (to prove D-017: it must **never** appear) · Kotlin: `suspend`, extension functions, default arguments, `data`/`sealed`/`value`/`object`/companion, `@JvmName`, `@JvmStatic`, nullable types, properties with custom accessors, typealiases, inline/reified. Requirements: - **Deterministic builds** — same bytes on repeat runs, or golden tests become flaky. - **Self-describing** — fixtures carry `@ExpectedMembers`-style annotations (§5.2) so adding a fixture adds test coverage automatically. - **A `Fixtures` helper** resolves jar paths with no hard-coded absolute paths. ### 11.2 The v1/v2 diff pair (issue \#23) `jdx diff` needs a corpus that is two *artifacts* rather than one, so `testfixtures` carries a second, independent pair: `src/diffV1/` and `src/diffV2/`, **the same package** `dev.jdx.diffapi` compiled into `build/diff-fixtures/diff-fixtures-v1.jar` and `-v2.jar` and resolved by `dev.jdx.testsupport.fixtures.DiffFixtureJars` (property `jdx.diffFixturesDir`). Same package on both sides is the whole point: version the package and every type reads as a removal plus an addition, and the intra-type rules never fire. Three rules make it work without disturbing the main corpus: - **A different directory, and a base name that does not start with `testfixtures-`.** `FixtureJars`, core's `Fixtures` and `ArtifactTestJars` all `require(size == 1)` over `build/libs`, so a second jar there turns three unrelated assertions into exceptions. - **A third source set, `src/diffAbsent/`, that compiles but is never packaged.** `javac` refuses a class whose supertype is missing, so the fixture for "the supertype is in neither artifact" has to exist in order to compile and then be kept out of both jars — which is exactly the real-world case (a superclass in *another dependency*) the differ's `inheritance not checked` detail exists for. - **A deliberately unchanged type** (`Stable.java`, byte-for-byte in both trees), so the pair can prove the differ is *not noisy* as well as not blind. **Rule coverage, not `@ExpectedMembers`.** The pair is self-describing through `DiffFixtureRuleCoverageTest` in `:index` rather than through per-type annotations: the property worth pinning is a property of the *pair* — that every rule in `core/diff/CompatRule.kt` fires against **real `javac` output**. A rule can pass every hand-built example in `core` and still be unreachable, because the reader never produces the shape it expects; that is the one class of bug this corpus exists to catch. The test lists every rule with the fixture that triggers it, so a new rule with no fixture fails the build — and a rule that fires with no fixture fails it too, so a fixture cannot grow silently. It also carries an explicit `unreachableFromJavac` list, with reasons, for rules that are correct but that no compiler will produce input for — `VARARGS_CHANGED` is the example, because `f(String)` and `f(String...)` have *different* descriptors, so that change is a `PARAMETER_TYPE_CHANGED`. Those are pinned by hand-built snapshots instead. Adding one to the required list "to make it pass" would claim coverage the corpus does not have. ### 11.1 Adding a fixture 1. Add the source under `testfixtures/src/main/java` (or `src/main/kotlin`). The odd one out is `src/nodebug/java`: classes there compile with `-g:none` (see `testfixtures/build.gradle.kts`), so only a fixture that must lack debug info goes there. 2. Annotate **every** source-declared type with `@ExpectedMembers` — one entry per declared field, method and constructor, exactly as `javap -p` prints the member line (modifiers plus fully-qualified signature, no trailing `;`). List synthetic members (`this$0`, bridges, `$default` stubs) like any other; never list `static {}`. A type declaring nothing carries an explicit empty list. Anonymous/local classes, enum constant bodies and the Kotlin file facade cannot carry the annotation — they live in `FixtureCorpusTest`'s pinned exempt set instead, which you must extend consciously. 3. Run `./gradlew :testfixtures:jar` and copy the member lines from `javap -p -classpath testfixtures/build/libs/testfixtures-.jar ` into the annotation — never hand-write them from memory. The corpus test diffs annotation against `javap` member-for-member and fails on any deviation, including order. 4. If the fixture must never execute (a static-initialiser probe like `StaticInitMarker`), say so in its KDoc, and never reference it from test code — read bytes via `Fixtures.classBytes(...)`, never reflection (D-017). 5. Rebuild twice and compare sha256 of both jars; identical bytes are the acceptance bar. ### 11.3 Resolving the corpus from a test `dev.jdx.testsupport.fixtures.FixtureJars` is the **single** copy of the resolution, and `DiffFixtureJars` the single copy for the v1/v2 pair. Both expose a non-throwing `*CorpusAvailable()` / `available()` probe next to the throwing resolvers, and a suite that needs the corpus gates on it: ```kotlin private fun fixtureSourcesJar(): File { assumeTrue( FixtureJars.sourcesCorpusAvailable(), "no testfixtures corpus: build :testfixtures first (docs/TESTING.md §11)", ) return FixtureJars.sourcesJar() } ``` Ask, then resolve — never resolve and catch. A missing corpus is a property of the machine, so it deserves a visible SKIP; a stack trace through `singleJar` reads like a product defect and, in a mutation pre-scan, is indistinguishable from a real failure (§10.1, issue #66). **Do not write your own resolver.** `FixtureResolutionOwnershipTest` in `:testfixtures` scans every module's test sources for the walk-up marker and fails on any copy outside `FixtureJars` and core's `Fixtures` (core's test source set must not depend on a project — that is the one documented exception). Before adding a copy, ask whether the module can take `testImplementation(testFixtures(project(":testfixtures")))`; every module that reads the corpus already does. --- ## 12. What we deliberately do **not** test Testing time is finite; spend it where bugs live. - **Third-party libraries.** We do not test that ASM parses class files or that SQLite commits transactions. We test *our use* of them. - **Exact decompiler output text.** §5.3 explains the alternative. - **Rendering cosmetics** beyond what golden files already pin. Do not write assertions on individual spaces. - **Getters, `data class` members, trivial delegation.** Mutation testing will flag these as uncovered; that is acceptable and expected. - **Gradle's own behaviour.** --- ## 13. Running the tests ```bash ./gradlew test # tier 1 — fast, the TDD loop (<30s) ./gradlew check # tier 2 — + property, golden, fault injection (<3min) ./gradlew check -Pci # CI mode: budgets relaxed (report-only), same gates otherwise ./gradlew test -Pgolden.update=true # rewrite golden files — THEN READ THE DIFF ./gradlew soak # tier 3 — corpus, needs local jars ./gradlew soak -Pcorpus=~/.m2 # ...against your own corpus ./gradlew bench -PconfirmTier4=true # tier 4 — performance budgets (PROPOSAL §15) ./gradlew mutationTest -PconfirmTier4=true # tier 4 — mutation score ./gradlew testReport # aggregated HTML report ./gradlew verifyPitestWiring # the PIT corpus wiring gate (also runs in every `check`) ``` **Definition of done for any task:** tier 1 and tier 2 green; tier 3 green if you touched artifact reading, indexing, or rendering; new behaviour covered by at least one family from §2 that *generates* cases (2, 3, 5, 6, 7) rather than only hand-written examples. --- ## 14. A note on updating golden files `-Pgolden.update=true` is the most dangerous command in this repo. It makes every failing output test pass, instantly, whether or not the new output is correct. **Always read the resulting diff before committing it.** A golden file you updated without reading is a test you deleted. If the diff is large, that is a signal to check whether you changed behaviour you did not intend to change — not a signal to skim faster. All golden suites share one helper — `GoldenFiles.verifyAll` with the `UnifiedDiff` renderer (`testfixtures/src/testFixtures/.../testsupport/golden/`, T-054). Do not hand-roll comparison, update-mode, or orphan logic in a new suite; consume the helper via `testFixtures(project(":testfixtures"))`. --- # Built by agents jdx is a tool for AI agents that is itself written almost entirely by AI agents, working under rules a human owner sets. This page describes how that works — the guardrails are what make the result trustworthy. ## Agents do not share memory — so the repository has to Each contribution starts from a cold context. Nothing an earlier agent "knew" survives unless it is written down, so the repository is organised to be resumable by a stranger: - [`AGENTS.md`](https://mohammadmd1383.github.io/jdx/docs/agents-md/) is the entry point: what the project is, the non-negotiable principles, decisions the owner has locked (and how to reverse one), exit codes, build and test rules. - Every module has its own `AGENTS.md` with its key files, invariants and known gotchas — an agent reads only the notes for the code it touches. - [`CONTRIBUTING.md`](https://mohammadmd1383.github.io/jdx/docs/contributing/) defines "done" for a command: behaviour, adapters, text and JSON renderers, exit codes, truncation, golden tests, a generative test family, documentation and an issue update. - [GitHub Issues](https://github.com/MohammadMD1383/jdx/issues) are the roadmap and the list of known limitations. A note that goes stale is treated as a bug: a change that invalidates a note updates it in the same commit. ## Tests that do not trust examples Hand-written examples only test what the author thought of. jdx requires a *generative* test family for every behaviour, on top of examples: - **Differential** — answers are checked against `javap` across real jars. - **Property-based** — e.g. every printed reference parses back to itself. - **Metamorphic** — equivalent queries must give equivalent answers. - **Fault injection** — corrupt jars, truncated class files, unreadable caches. - **Corpus soak** — thousands of real jars, including a large obfuscated one. - **Mutation testing** — the core must kill at least 80 % of mutants; line-coverage gates on the other engine modules. The full strategy is in the [testing guide](https://mohammadmd1383.github.io/jdx/docs/testing/). ## Dogfooding Agents working on jdx use jdx on jdx — its own jars, its dependencies, the JDK — instead of `grep` and `javap`. A wrong answer, crash or missing capability found that way is filed as an issue in the same session. The tool improves exactly where agents struggle with it. ## Docs that cannot drift This website is generated from the repository on every change to `main`, and it is built to fail rather than lie: - every command example on it is **executed** against the freshly built jdx, and the build fails if a command no longer works or its exit code changed; - the command and MCP reference pages are generated from `jdx --help` and the MCP server itself; - every `jdx --flag` mentioned in the docs is checked against the real help text; - every internal link and anchor is resolved. So when an agent changes the CLI and forgets the docs, the site build goes red on the pull request instead of publishing something stale. ## The human's job The owner sets direction, settles design questions, approves anything outward-facing (releases, public repository changes) and reviews the results. Agents are expected to ask when something is genuinely ambiguous rather than guess — the same rule jdx itself follows. --- # AGENTS.md — jdx > **Entry point.** This file is the rules. Then read only what your change needs: > the module `AGENTS.md` (`core/`, `index/`, …), GitHub Issues (roadmap), > `docs/TESTING.md` (before writing tests), `CONTRIBUTING.md` (before writing > code). The full design lives in `docs/PROPOSAL.md` — read it lazily. > (The v1 task board, session logs, and decision/lesson shards were removed > post-v1; their durable content is distilled here and in the module notes, > full history in git.) ## 1. What this project is `jdx` is **an IDE for AI agents, as a command line tool.** An agent working on a JVM codebase constantly needs to know things that today cost it many shell round-trips: what methods a class has, what a method actually does, who calls it, what implements an interface. Today it does this with `unzip`, `javap`, `grep`, and by reading 1000-line source files into its context window. That is slow, noisy, and burns context. `jdx` answers those questions directly, from `.jar`, `-sources.jar`, class directories, source directories, Maven coordinates, and the JDK itself: ``` jdx members com.google.gson.Gson --inherited # what can I call on this? (IDE ".") jdx body 'com.google.gson.Gson#toJson(Object)' # show me ONLY that method body jdx usages com.google.gson.TypeAdapter # find usages jdx hierarchy java.util.Map # who implements this? jdx search '*Http*Client' --kind class # symbol search ``` **Design north star:** every command answers one precise question with the *smallest correct output*, and tells the agent exactly what to type next. Never dump a whole file when a member was asked for. **v1 is complete** (all commands in `jdx help --agent` ship with text + `--json`, exit codes, goldens). ## 2. Non-negotiable principles 1. **Minimal correct output.** Consumed by a model with a finite context window. Truncate intelligently and say how to get more — never silently, never by dumping more. 2. **Never guess a symbol.** Ambiguous input exits `2` with every candidate as a copy-pasteable canonical ref. Guessing wrong is worse than asking. 3. **Every output line names its symbol canonically**, so the next command is a mechanical copy-paste, not a reconstruction. 4. **Provenance always available.** Which jar, which sources jar, bytecode vs source vs decompiled, which decompiler. An agent must be able to judge confidence. 5. **Deterministic.** Same inputs → byte-identical output. Sorted. No ANSI unless TTY. No timestamps, no absolute paths, no hash values in default output. 6. **Meaningful exit codes** (see §6) so agents can branch without parsing prose. 7. **Fast.** Cold CLI under ~250 ms; warm daemon under ~20 ms. Called dozens of times per task. 8. **Degrade, don't fail.** No sources jar → decompile. Decompiler fails → `javap`. Kotlin metadata corrupt → JVM view. Always emit *something*, and say what it is. 9. **Heavily tested, by construction.** `core` test-first; everything else backed by *generative* families — differential against `javap`, property-based, metamorphic, fault-injection, corpus soak. Hand-written examples alone are not sufficient. ## 3. Locked rules (owner-settled — do not re-litigate; to change one, open an issue, get an explicit reversal, record it here and in the commit message) - **Truth model:** structure from bytecode, flesh (bodies, param names, docs) from sources; disagreement warns `SOURCES_VERSION_MISMATCH`, never silently wins. - **Read-only:** parse with ASM, never load inspected classes (no static init ever runs). Write only inside the platform dirs (`docs/PROPOSAL.md` §17.1 table). - **Per-OS dirs (D-044, issue #44):** cache/config/socket/install defaults per OS — Linux `~/.cache|~/.config|$XDG_RUNTIME_DIR` (honouring `XDG_*`); macOS `~/Library/Caches|~/Library/Application Support|$TMPDIR/jdx-$UID`; Windows `%LOCALAPPDATA%/jdx/cache|%APPDATA%/jdx|%LOCALAPPDATA%/jdx/run` (AF\_UNIX, Win10 17063+ floor); install `~/.local/share/jdx` + `~/.local/bin` (same on macOS), `%LOCALAPPDATA%/Programs/jdx` + PATH shim on Windows. Supersedes D-013/D-027; a missing `XDG_RUNTIME_DIR` falls back per table, never `exit 3`. Precedence: CLI flag > `JDX_*` \> `XDG_*` \> OS default. Migration: move (never merge) the old location on > first run, warn if both exist. Full table in `docs/PROPOSAL.md` §17.1. - **Output:** text default, `--json` envelope complete (no text-only information). - **Exit codes are a public contract** — changing them breaks agents. - **Graph queries are live-roots-first:** `usages`/`hierarchy`/`callers`/`calls`/ `samples` re-scan bytecode roots per query (no persistent-index acceleration yet). - **Network is opt-in per invocation** (`--fetch`); coordinates resolve from `~/.gradle/caches` + `~/.m2` first. - **Push rule:** no `git push` / `gh repo create` without explicit owner go-ahead in the current conversation. |Area|Rule| |---|---| |Language / build|**Kotlin + Gradle** (JDK 21 toolchain target)| |Bytecode reading|**ASM**| |Decompiler|**Vineflower bundled** (default); `--engine javap` for raw opcodes| |Java sources|**JavaParser**| |Kotlin sources|**kotlin-compiler-embeddable PSI** — side-loaded into an isolated classloader, never a compile dep, never in the fat jar| |Kotlin binaries|`@Metadata` via **kotlin-metadata-jvm** → true Kotlin signatures| |Index|**Persistent on-disk index** (SQLite), content-hash keyed, behind an `IndexStore` interface| |Usages scope|**All workspace jars + the project's own source dirs**| |Classpath input|explicit paths/globs/dirs + **named workspaces** + **project auto-discovery** (never run Gradle/Maven) + **Maven coords (auto-download opt-in)** + **JDK stdlib via jrt-fs**| |Interfaces|one-shot **CLI**, **MCP** stdio server, **daemon** (5-min idle auto-shutdown), **HTTP/JSON** — all thin adapters over one `JdxService`, no logic in adapters| |Testing|**TDD mandatory in `core`** + generative families; Pitest mutation gate ≥ 80 % on `core`| |Deferred|mappings/remap, resources \& services inspection, `--since`/API-level reporting| |Repo|local git; public GitHub repo ([github.com/MohammadMD1383/jdx](https://github.com/MohammadMD1383/jdx)), Apache-2.0| ## 4. Repository layout ``` jdx/ ├── AGENTS.md ← you are here ├── README.md user-facing intro (hook, demo, install, quickstart, links) ├── docs/COMMANDS.md user-facing command catalogue (moved out of README) ├── CONTRIBUTING.md conventions, code style, definition of done ├── /AGENTS.md per-module notes — read only the one(s) you touch ├── docs/ │ ├── PROPOSAL.md full design document (the spec) │ ├── TESTING.md testing strategy — read before writing tests │ ├── guide/ user guides (website "Getting started" + "Guides" sections) │ └── FAQ.md user FAQ (website /faq/, FAQPage structured data) ├── gradle/libs.versions.toml version catalog (single source of dependency versions) ├── core/ model, symbol refs, resolution, rendering. Pure Kotlin, no IO. ├── index/ ASM readers, Kotlin metadata, SQLite store, indexer, JdxService ├── sources/ sources-jar handling, JavaParser, Kotlin PSI (isolated CL) ├── decompile/ Vineflower + javap engines ├── cli/ Clikt commands, text/JSON renderers ├── mcp/ MCP stdio server ├── server/ HTTP/JSON API + daemon ├── app/ fat-jar assembly + `jdx` launcher script ├── testfixtures/ nasty Java/Kotlin classes → binary jar + -sources.jar └── site/ GitHub Pages website generator (build-time only; site/AGENTS.md) ``` **Dependency rule:** `core` depends on nothing project-local. Everything depends on `core`. `cli`/`mcp`/`server` are ADAPTERS and must contain no behaviour; logic tempted into a Clikt command belongs in `core` or a service class so all four interfaces share it. ## 5. Symbol reference syntax (canonical) Javadoc style. Accept generously, print canonically. ``` com.google.gson.Gson type com.google.gson.Gson$TypeAdapterRuntime nested (also accepts .Inner) com.google.gson.Gson#toJson all overloads com.google.gson.Gson#toJson(Object) simple param names OK com.google.gson.Gson#toJson(java.lang.Object) fully-qualified params com.google.gson.Gson#toJson(Ljava/lang/Object;)Ljava/lang/String; exact JVM descriptor com.google.gson.Gson#(...) constructor Gson#toJson short form → resolved, or exit 2 w/ candidates ``` `::` and `.` are accepted in place of `#`. **Always quote the ref in shell examples** — `#` and `(` are shell-hostile. `$` always separates nesting; the final dot-segment is always the class. `parse(print(ref)) == ref` is a pinned property — don't break round-tripping. ## 6. Exit codes (contract — do not change casually) |Code|Meaning| |---|---| |`0`|Success, results found| |`1`|Valid query, **no results** (class/member not found)| |`2`|**Ambiguous** reference — candidates printed| |`3`|Usage / argument error| |`4`|No index or workspace resolved for this query| |`5`|Artifact read error (corrupt jar, unreadable file)| |`6`|Internal error| **The one documented exception** (owner-approved, issue #23): `jdx diff` exits `0` whenever the comparison ran, whatever it found, and `--fail-on breaking|any` exits `1` when the gate trips. A diff has no "not found" outcome, and the verdict is also in the envelope as `result.gate`, so a consumer that cannot read exit codes still gets the answer. Nothing else may bend this table. ## 7. Build, test, lint - Build only via the Gradle wrapper (`./gradlew`); JDK 21+ required. Categories: `test` (fast unit loop, per-test budget \<= 2.5 s) · `integrationTest` (integration suites, per-test budget \<= 120 s) · `check` (pre-commit: unit + integration + lint + coverage) · `soak`/`bench`/`mutationTest` on demand (`bench` and `mutationTest` require explicit confirmation via `-PconfirmTier4=true` or `-PallowHeavy=true`). - `core` TDD-mandatory, mutation gate ≥ 80%; `index`/`sources`/`decompile` ≥ 85% line; adapters ungated (parity + goldens instead — gating thin code incentivises padding). - **A gate that cannot run is not a gate.** `check` runs `verifyPitestWiring` (alongside `lint`) because the `core` mutation gate aborts in PIT's *pre-scan* — silently, with no score produced at all — when the fixture corpus is missing, and that only shows up on a clean checkout. Any precondition a gate needs gets its own cheap assertion in `check`; don't let a red nightly be the thing that discovers it. `docs/TESTING.md` §10.1. - Every behaviour needs a *generative* family (property / `javap`-differential / metamorphic / fault-injection / corpus), not just examples. - Root `lint` (runs in every `check`): no trailing whitespace, no tabs, no bare `TODO`/`FIXME` without a reference, no `println`/`System.exit`/`printStackTrace` in library mains, `allWarningsAsErrors` on main + test compiles. - `-Pgolden.update=true` is the most dangerous command here: **read every golden diff before committing it.** An unread golden update is a deleted test. - CI mode `-Pci` (also auto-detected via `CI`/`GITHUB_ACTIONS`): wall-clock test gates relax on slow runners — per-test budget report-only, PIT timeouts 10 s → 60 s. Local loop unchanged. Product timeouts untouched. See `docs/TESTING.md` §2. - Line counting is POSIX: one trailing `\n` is a terminator, not a line. ## 8. Working agreements - **Keep GitHub Issues current** — they are the roadmap. Move finished items out, record newly found ones (limitations, follow-ups, caveats). A change that alters behaviour or finds a caveat and leaves them stale is incomplete. - **Keep the `AGENTS.md` notes true.** If a change invalidates a module note (new invariant, new gotcha, new key file), update that note in the same change. A contributor who trusts a stale note pays twice. - **Ask the owner about genuine ambiguity** rather than picking. A silent wrong guess propagates through a codebase nobody fully reads. - **Work in the cadence: one small change → commit → push → next.** Conventional Commits with a module scope: `feat(index): parallel artifact indexer`. If a change can't be finished and committed in one sitting, it is too big — **split it first.** An unpushed working tree is invisible to every other contributor. - **Definition of done for a command:** behaviour in `core`/`index`/`sources`/`decompile`, thin adapter in `cli`, `--help` text, text renderer, JSON renderer, exit codes, truncation, golden tests for both renderers, at least one *generative* test family, docs/COMMANDS.md table row, Appendix B flag entry, GitHub Issue filed/updated if behaviour or limitations changed, and `./gradlew :site:buildSite` green (the website executes doc examples and checks every documented flag against `--help` — see `site/AGENTS.md`). - **Report honestly.** Failing tests get pasted, not summarised away. Documented half-finished work is useful; half-finished work reported as done is a trap. - **Write for a stranger.** Explicit over clever, named over inlined, invariants stated in comments where types don't enforce them. - **Batch independent tool calls in a single turn.** Sequence calls only when one genuinely depends on another's output. ## 9. Specs and roadmap - `docs/PROPOSAL.md` — design spec (§6 ref grammar, §8 output, §14 serving, Appendix B flag reference, §19 licenses). Code cites `PROPOSAL.md §X`; keep true. - `docs/TESTING.md` — testing strategy; read before writing tests. - GitHub Issues — known limitations + phase-2 backlog (the roadmap). - docs/COMMANDS.md is the user-facing catalogue; every shipped flag must appear in `jdx --help`, the docs/COMMANDS.md table, and Appendix B. - **Website** ([mohammadmd1383.github.io/jdx](https://mohammadmd1383.github.io/jdx/)) is generated from README.md, docs/ and the built binary by `site/` and deployed by `.github/workflows/pages.yml` on every push to `main` and every release. Never edit it by hand; change the source. Hierarchy and URLs live in `site/.../SiteManifest.kt`. ## 10. Dogfood rule — built by agents, for agents `jdx` is its own proof of work: when working on this repo, use `jdx` on this repo first (own source dirs / built jars, third-party jars, the JDK) instead of `javap` / `grep` / `unzip` / full-file reads for structural questions. - A wrong answer, crash, or missing capability found while dogfooding is a bug or feature gap in `jdx` — file it as a GitHub Issue in the same session (`gh issue create`), with: the exact `jdx` command + inputs, when/where/why it happened, expected vs actual, the fallback used, and the proposed fix. - Never block on the fix: fall back to whatever works (`javap`, `grep`, direct reads) and finish the task. Never work around silently without filing — an unfiled dogfood bug is an incomplete task. ## 11. Environment notes (this machine) - Arch Linux. JDK at `/usr/lib/jvm/default` (`java`, `javac`, `javap`, `jar`, `jdeps`). `JAVA_HOME` is **unset** — the launcher must resolve the JDK itself and not assume it. Other OSes (CI-verified via `windows.yml` / `macos.yml`, issue #52): macOS JDK via `/usr/libexec/java_home -v 21` (override with `$JDX_JAVA_HOME_HELPER` in tests); Windows JDK via `%JAVA_HOME%\bin\java.exe` → `where java.exe` → registry (`JavaSoft` / `Adoptium`) → `%ProgramFiles%\Java` (see `app/AGENTS.md`). Windows symlinks need Developer Mode, otherwise launcher/installer symlink suites skip with a reason; goldens stay LF (`*.sh eol=lf`, `*.bat eol=crlf`) with CRLF-tolerant compare (`GoldenFiles.normaliseLineEndings`, `UnifiedDiff.splitLines`). - **No `mvn`/`gradle` on PATH.** Use the Gradle **wrapper**. - Maven Central and GitHub are reachable. `git` and `gh` are installed. - In the `/opencode` GitHub Actions job, `GH_TOKEN` is the job's own `GITHUB_TOKEN` scoped to `actions: read` on this repo — so `gh run list`, `gh run view --log`, `gh run watch` work. **Read the logs instead of guessing at a CI failure**, and never ask for a PAT to do it. - Rich local test corpus: ~2183 jars in `~/.gradle/caches`, including `-sources.jar` artifacts (gson, msal4j, lwjgl, mojang-logging) and a large obfuscated/remapped `minecraft-client.jar` under `~/.gradle/caches/fabric-loom/` — the scale/perf benchmark; gson is the everyday correctness fixture. - ASM and Vineflower already exist in the local Gradle cache. --- # Contributing to jdx This project is built by a rotating cast of contributors — humans and AI agents — who do not share memory, habits, or taste. Nobody here has read the whole codebase. **The conventions below exist so that code written by six different people looks like it was written by one.** If you are an AI agent, start with `AGENTS.md`, then read the [For AI agents](#for-ai-agents) section at the bottom of this file. --- ## The one-minute version ```bash git clone && cd jdx ./gradlew build # no system gradle/maven needed; the wrapper is the only entry point cat AGENTS.md # architecture rules, build/test/lint, pointers gh issue list --limit 40 # limitations and the phase-2 backlog — where new work starts # ... do the work ... ./gradlew check # tests + lint must pass # update GitHub Issues and the touched module's AGENTS.md if behaviour changed, commit ``` --- ## Where things are written down |Question|File| |---|---| |What is this project and what are its rules?|`AGENTS.md`| |Why is it designed this way? What does each command do?|`docs/PROPOSAL.md`| |Why was X chosen over Y? May I change it?|`AGENTS.md` (root locked rules + module notes)| |What should I work on next?|GitHub Issues (limitations, follow-ups, phase-2 backlog)| |What happened before I got here?|git history (the v1 board and session logs were removed post-v1)| |What mistakes have already been made here?|The module `AGENTS.md` gotchas (distilled post-v1; history in git)| |How do I write code that fits in?|this file| **Keep them accurate.** A stale doc in this project is worse than a missing one, because the next contributor has no way to tell the difference. --- ## Non-negotiables These are not style preferences. Violating one breaks the product. 1. **`core` has no dependencies.** No ASM, no SQLite, no JavaParser, no file IO, no network. It is the shared vocabulary and must stay trivially testable. If you need bytecode in `core`, you need a different design. 2. **Front-ends contain no logic.** `cli`, `mcp`, `server` parse input, call `JdxService`, render, and map exit codes. Nothing else. Four front-ends were a deliberate choice and they only stay consistent if they are all thin. If you catch yourself writing an `if` about *behaviour* in a Clikt command, move it. 3. **Never load an inspected class into the JVM**. Parse with ASM. Reflection on a third-party jar executes its static initialisers — that is a security property we sell. 4. **Never guess a symbol**. Ambiguous input exits `2` with candidates listed. 5. **Exit codes are a public contract**. Changing one is a breaking change. 6. **Text and JSON must carry the same information**. 7. **Output is deterministic.** Sorted. No timestamps, absolute paths, hashes, or ANSI in non-TTY output. Golden tests enforce this; do not weaken them to make a test pass. 8. **Every fallback is labelled.** Decompiled output says it is decompiled. A guessed parameter name says so. `jdx` must never present a reconstruction as ground truth. --- ## Code style We optimise for **a stranger reading this once, under time pressure**, not for elegance. - **Kotlin official style**, 4-space indent, 100-column soft limit. - **Explicit API mode** in `core` — every public declaration needs an explicit visibility and return type. - **Name things fully.** `resolveInheritedMembers`, not `resolve`. `sourcesJarPath`, not `sp`. Long names are free; re-reading a function to learn what `s` is, is not. - **Prefer boring code.** No operator overloading for domain types, no DSL builders for internal use, no reflection, no clever generic gymnastics. If a reviewer has to pause, it is too clever. - **Sealed classes over booleans + nulls** for states with a closed set of cases (`Provenance`, `SymbolRef`, query results). Kotlin's exhaustive `when` is the main reason we chose Kotlin; use it. - **Errors are values, not exceptions**, on any path an agent can trigger. Exceptions are for bugs. A missing class is a `Result`, not a throw. - **Comment the invariant, not the syntax.** `// callers rely on this list being sorted by declaring-type depth` is useful. `// loop over members` is noise. - **Every public type gets one KDoc line** saying what it represents and who produces it. That single line is what lets a newcomer navigate without reading call sites. ### Comment density Match the surrounding file. In `core/resolve/` (subtle algorithms) comments are dense and explain *why*. In `cli/commands/` (mechanical wiring) they are nearly absent, because the code says everything. Do not sprinkle uniform comment density across both. --- ## Adding a command — the checklist A command is **not finished** until all of these are true. This list is the most common source of half-done work. - \[ \] Behaviour implemented in `core`/`index`/`sources`/`decompile`, exposed via `JdxService` - \[ \] Clikt command in `cli`, thin, with complete `--help` text (agents read `--help`) - \[ \] **Text renderer** following the layout conventions in `docs/PROPOSAL.md` §8 - \[ \] **JSON renderer**, same information, inside the standard envelope - \[ \] Exit codes per the public contract, including the ambiguity path - \[ \] Truncation handled: bounded output with `shown`/`total`/`hint` - \[ \] `next:` hint line on single-entity output, where a sensible follow-up exists - \[ \] **Golden tests** for text and JSON against the fixture corpus - \[ \] MCP tool entry — generated from shared metadata, not hand-written twice - \[ \] Row in the docs/COMMANDS.md command table - \[ \] Flag documented in `docs/PROPOSAL.md` Appendix B - \[ \] GitHub Issue filed/updated if behaviour or limitations changed - \[ \] `./gradlew :site:buildSite` green — the website runs every `console exec` doc example and checks documented flags against `--help`; update `docs/guide/` if users need to know --- ## Testing **Read [`docs/TESTING.md`](https://mohammadmd1383.github.io/jdx/docs/testing/) before writing tests.** It is the authoritative strategy; this is the two-minute version. - **`core` is written test-first.** Red → green → refactor, no exceptions. It has no dependencies and no IO, so "hard to test" is never true there. - **Hand-written examples are not enough.** They plateau at what you already thought of. Every new behaviour also needs at least one test family that *generates* its own cases: property-based, differential-vs-`javap`, metamorphic, fault-injection, or corpus soak. - **Fixtures over mocks.** The `testfixtures` corpus compiles real, nasty Java and Kotlin at build time. Hand-built `ClassInfo` objects encode your assumptions — and your assumptions are exactly what the test should be checking. There is no mocking framework in `core`; if you need one, push the IO outward instead. - **Use the oracles.** `javap -p -s` tells you the truth about any class file without you writing it down. For decompilation, assert that the output **recompiles to the same API**, never that it matches particular text. - **Test names are sentences:** ``fun `inherited members from a generic supertype are substituted`()``. The test report should read as a specification of what `jdx` does. - **Every bug fix starts with a failing regression test** named after the issue. - **Respect the tiers.** `./gradlew test` must stay under 30 s — anything touching disk, network or a real jar belongs in tier 2 or above. A slow inner loop kills TDD, and then it kills testing. - **A gate that cannot run is not a gate.** If a check can only fail under a condition you never reproduce locally — a clean checkout, a bare CI runner — it will be discovered by a red nightly months later, or never. Give the precondition its own cheap assertion in `check`. `verifyPitestWiring` is the worked example: the `core` mutation gate silently produced no score at all in CI (issue #66), because PIT's pre-scan aborts before mutating anything when the fixture corpus is missing. - **Ask, then resolve.** A missing input is a property of the machine, not a defect. Test helpers expose an availability probe and the suite `assumeTrue`s on it, so absence is a visible SKIP rather than a red whose stack trace points at product code (`docs/TESTING.md` §10.1, §11.3). ```bash ./gradlew test # tier 1 — the TDD loop (<30s) ./gradlew check # tier 2 — pre-commit (<3min) ./gradlew soak # tier 3 — real jar corpus (tagged, needs local jars) ./gradlew bench mutationTest -PconfirmTier4=true # tier 4 — performance budgets and mutation score ./gradlew test -Pgolden.update=true # rewrite goldens — THEN READ THE DIFF ``` > **The golden-file hazard.** `-Pgolden.update=true` makes every failing output test pass > instantly, correct or not. **Always read the diff before committing it.** A golden file you > updated without reading is a test you deleted. A large diff is a signal to check whether you > changed behaviour you didn't mean to — not a signal to skim faster. ## Commits and branches - **Conventional Commits:** `feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `build:`, `perf:`, `chore:`. Scope with the module: `feat(index): parallel artifact indexer`. - **The cadence is: one small change → commit → push → next.** Not "finish everything, then commit". Contributors here share no memory; an unpushed working tree is invisible to everyone else, and if the session ends there the work is effectively lost. Small pushed increments also keep `git bisect` useful and let a bad change be reverted without unpicking four others. - **Small, working increments.** Every commit should build. A 2,000-line commit cannot be reviewed and cannot be bisected. - **If a change won't fit in one sitting, split it first**, then do the first half. That is normal, not an admission of failure. - Branch per change: `outline-command`, `daemon-log-gc`. - **Never push to a remote without an explicit go-ahead** from the project owner in the current conversation/session. --- ## Changing a locked rule `AGENTS.md` (root) marks which rules are owner-settled (`locked`) and which are defaults. - **Defaults** — improve them in a normal PR and update the note in the same PR. - **`locked`** — the project owner decided them explicitly, usually after being shown the alternatives. Do **not** change them, and do not erode them gradually through refactors. If you think one is wrong, open an issue making the case, get an explicit reversal, and record it in `AGENTS.md` (and the commit message). Never silently re-litigate a settled rule. The history is the value. --- ## For AI agents You are a first-class contributor here, and this project is designed around the assumption that you have no memory of yesterday. **Your session is only complete when the documentation is.** Concretely: 1. **Read before writing.** `AGENTS.md` → the module `AGENTS.md` for your area → GitHub Issues. Roughly 15 minutes. It will save you from re-deciding things that are already settled and from repeating paid-for mistakes. 2. **Ask the owner about genuine ambiguity instead of picking.** This is an explicit standing instruction from the owner. A wrong guess propagates silently through a codebase that nobody fully reads. 3. **Update GitHub Issues (always on).** Move finished items out, record newly found ones — deferred follow-ups, environment caveats, limitations. A change that alters behaviour or finds a caveat and leaves it stale is incomplete. 4. **Update the touched module's `AGENTS.md`.** New invariant, new gotcha, new key file — write it down in the same change, compressed. The next agent reads it cold and trusts it. 5. **Report honestly.** If tests fail, say so and paste the output. If you skipped part of a task, say which part and why. Half-finished work that is *documented* as half-finished is useful; half-finished work reported as done is a trap that costs the next contributor more than the work was worth. 6. **Do not expand scope silently.** If you find adjacent work, file an issue; do not fold it into the current change. 7. **Leave the build green.** If you cannot, say so loudly, with the exact failing command. --- # Agent cheat sheet Paste this into your agent's instructions (`CLAUDE.md`, `AGENTS.md`, `.cursorrules`, a system prompt) so it reaches for jdx instead of `unzip`, `javap` and `grep`. It is the verbatim output of `jdx help --agent`, regenerated from the current build. ```console $ jdx help --agent jdx agent cheat sheet (paste into CLAUDE.md / system prompt): jdx show — Go to declaration jdx members --inherited — Code completion after `.` jdx outline — File structure jdx signature — Parameter info jdx doc — Quick documentation jdx body — Open the method jdx source — Open the file / decompile jdx search — Search everywhere jdx resolve — What is this symbol? jdx ls [package] — External library browser (packages/types) jdx tree [artifact] — External library browser (artifacts) jdx usages — Find usages jdx hierarchy — Type hierarchy jdx implementors — Who implements this? jdx callers — Call hierarchy (in) jdx calls — Call hierarchy (out) jdx samples — Real call sites as usage examples jdx diff — Public-API diff + binary-compat warnings (upgrades) jdx version — Print the jdx version jdx upgrade [--check] — Self-update from GitHub Releases jdx doctor — Self-diagnosis (JDK, index, AppCDS, daemon, workspace) jdx ws create|list|info|remove|add|use — Project and library configuration jdx cache info|gc|clear — Index and cache maintenance jdx kotlin install — Fetch the Kotlin PSI sidecar (Kotlin sources) jdx daemon start|stop|status|restart — Warm background JVM (5-min idle shutdown) jdx serve [--port 7070] — Local HTTP/JSON API jdx setup --agent opencode|claude-code|cursor|kilo|cline|codex|copilot|antigravity --scope project|system — Wire jdx into an agent (OpenCode, Claude Code, Cursor, Kilo Code, Cline, Codex CLI, GitHub Copilot CLI, Antigravity) jdx mcp — MCP stdio server (typed jdx_* tools) jdx batch — Many queries, one process (NDJSON in/out) jdx bench [--iterations 3] — Benchmark the read path (timings + §15 targets) jdx help [--agent] — This cheat sheet refs: com.example.Outer#method(Type), Outer#method, 'Owner#m(Object)' (quote: # and () are shell-hostile) exit codes: 0 ok · 1 not found · 2 ambiguous (retry with a listed candidate) · 3 usage · 4 no workspace · 5 artifact error · 6 internal add --json to any command for the structured envelope (same information) ``` --- # MCP tools `jdx mcp` serves 21 typed tools over the Model Context Protocol (stdio). This list is what the server itself answers to `tools/list` in the current build. > jdx — an IDE for AI agents: Java/Kotlin structure, sources, usages and call graphs, one precise question per tool. Every answer carries provenance; ambiguous refs list candidates instead of guessing. ## `jdx_show` Go to declaration: the class card for a type (kind, supertypes, source, provenance). | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | ## `jdx_members` List the members of a type — the '.' completion equivalent. Inherited members are included by default, grouped by declaring type. | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `kind` | string | Member kind to list: method, field, ctor, property or all (default all). | | `access` | string | Visibility to list: public, protected, package, private or all (default: public + protected). | | `static` | boolean | Only static members (mutually exclusive with instance). | | `instance` | boolean | Only instance members (mutually exclusive with static). | | `from` | string | Only members inherited from this supertype. | | `grep` | string | Only members whose name matches this regex. | | `includeSynthetic` | boolean | Include bridge/synthetic members, hidden by default. | | `limit` | integer | Maximum rows shown (default 50); the rest become a truncation footer. | | `withDoc` | boolean | Include the first javadoc sentence per member. | | `sort` | string | Row order: kind (the default), name (flat name-first) or declaring. | | `view` | string | Declaration projection: kotlin (the default, true Kotlin declarations from @Metadata) or jvm (the raw JVM projection). | | `declared` | boolean | Only members declared on this exact type (overrides the inherited default). | ## `jdx_outline` File Structure: one dense line per member declared on a type (never inherited). | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `kind` | string | Member kind to list: method, field, ctor, property or all (default all). | | `access` | string | Visibility to list: public, protected, package, private or all (default: public + protected). | | `static` | boolean | Only static members (mutually exclusive with instance). | | `instance` | boolean | Only instance members (mutually exclusive with static). | | `from` | string | Only members inherited from this supertype. | | `grep` | string | Only members whose name matches this regex. | | `includeSynthetic` | boolean | Include bridge/synthetic members, hidden by default. | | `limit` | integer | Maximum rows shown (default 50); the rest become a truncation footer. | | `withDoc` | boolean | Include the first javadoc sentence per member. | | `sort` | string | Row order: kind (the default), name (flat name-first) or declaring. | | `view` | string | Declaration projection: kotlin (the default, true Kotlin declarations from @Metadata) or jvm (the raw JVM projection). | ## `jdx_body` Open the method: the source body of one member, sliced verbatim with provenance. | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `context` | integer | Extra lines around the body (default 0). | | `lineNumbers` | boolean | Prefix every line with its 1-based number. | | `maxLines` | integer | Cap the body slice (default 200). | | `withSignature` | boolean | Prepend the member signature line. | | `withDoc` | boolean | Append the member javadoc. | | `engine` | string | Force a reconstruction engine: vineflower or javap (default: paired sources first). | ## `jdx_source` Open the file: whole source for a type, or a slice of it, with provenance. | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `lines` | string | Line window A:B (1-based, inclusive). | | `around` | string | Center the slice on this member ref. | | `context` | integer | Extra lines around the slice (default 0). | | `lineNumbers` | boolean | Prefix every line with its 1-based number. | | `maxLines` | integer | Cap the slice (default 200). | | `engine` | string | Force a reconstruction engine: vineflower or javap (default: paired sources first). | ## `jdx_signature` Parameter info: one signature line per overload, with real parameter names. | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `includeSynthetic` | boolean | Include bridge/synthetic members, hidden by default. | | `limit` | integer | Maximum rows shown (default 50); the rest become a truncation footer. | | `view` | string | Declaration projection: kotlin (the default, true Kotlin declarations from @Metadata) or jvm (the raw JVM projection). | ## `jdx_doc` Quick documentation for a type or member, falling back to the nearest documenting supertype like IntelliJ quick-doc. | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `inherited` | boolean | Allow the supertype fallback (the default; accepted for explicitness). | | `no-inherited` | boolean | Only docs written on this exact symbol (mutually exclusive with inherited). | | `raw` | boolean | Raw javadoc text instead of the rendered form. | | `maxLines` | integer | Cap the doc text (default 200). | ## `jdx_search` Search everywhere: symbols by glob, regex or fuzzy name, with did-you-mean. | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `kind` | string | Symbol kind: all, type, class, interface, enum, record, annotation, object, companion, method, field, package or module (default all). | | `regex` | boolean | Treat the pattern as a regex. | | `fuzzy` | boolean | Levenshtein fallback when nothing matches. | | `in` | string | Only matches from artifacts whose label matches this glob. | | `package` | string | Only symbols under this package. | | `limit` | integer | Maximum rows shown (default 50); the rest become a truncation footer. | ## `jdx_resolve` What is this symbol? Short-name resolution with copy-pasteable candidates. | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `limit` | integer | Maximum rows shown (default 50); the rest become a truncation footer. | ## `jdx_ls` External library browser: packages in the workspace roots. | Parameter | Type | Description | |---|---|---| | `query` | string | Package glob (ls) or artifact glob (tree); empty lists everything. | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `limit` | integer | Maximum rows shown (default 50); the rest become a truncation footer. | ## `jdx_tree` External library browser: artifacts and their package trees. | Parameter | Type | Description | |---|---|---| | `query` | string | Package glob (ls) or artifact glob (tree); empty lists everything. | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `depth` | integer | Tree depth (default 8). | | `counts` | boolean | Annotate nodes with class counts. | | `limit` | integer | Maximum rows shown (default 50); the rest become a truncation footer. | ## `jdx_usages` Find usages of a type or member across the workspace jars and source dirs. | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `kind` | string | Edge kind: all, call, read, write, ref, new, throw (throws declarations, not ATHROW sites) or annotation (default all). | | `in` | string | Only matches from artifacts whose label matches this glob. | | `exclude` | string | Skip matches from artifacts whose label matches this glob. | | `limit` | integer | Maximum rows shown (default 50); the rest become a truncation footer. | | `context` | integer | Snippet lines around each hit (default 0). | ## `jdx_hierarchy` Type hierarchy: supertypes upward and subtypes downward across the workspace. | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `up` | boolean | Walk supertypes (default: both directions unless down is set). | | `down` | boolean | Walk subtypes (default: both directions unless up is set). | | `direct` | boolean | Direct parents/children only. | | `depth` | integer | How many tree levels to walk (default 2147483647). | | `in` | string | Only matches from artifacts whose label matches this glob. | | `exclude` | string | Skip matches from artifacts whose label matches this glob. | | `limit` | integer | Maximum rows shown (default 50); the rest become a truncation footer. | ## `jdx_implementors` Who implements this interface or extends this class, transitively. | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `direct` | boolean | Direct implementors only. | | `depth` | integer | How many tree levels to walk (default 2147483647). | | `in` | string | Only matches from artifacts whose label matches this glob. | | `exclude` | string | Skip matches from artifacts whose label matches this glob. | | `limit` | integer | Maximum rows shown (default 50); the rest become a truncation footer. | ## `jdx_callers` Call hierarchy in: who calls this method, as a depth-bounded tree. | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `depth` | integer | How many tree levels to walk (default 1). | | `in` | string | Only matches from artifacts whose label matches this glob. | | `exclude` | string | Skip matches from artifacts whose label matches this glob. | | `limit` | integer | Maximum rows shown (default 50); the rest become a truncation footer. | ## `jdx_calls` Call hierarchy out: what this method calls, as a depth-bounded tree. | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `depth` | integer | How many tree levels to walk (default 1). | | `in` | string | Only matches from artifacts whose label matches this glob. | | `exclude` | string | Skip matches from artifacts whose label matches this glob. | | `externalOnly` | boolean | Prune callees in the target's own artifact (dependency view). | | `limit` | integer | Maximum rows shown (default 50); the rest become a truncation footer. | ## `jdx_samples` How is this symbol really used? Ranked call sites with source snippets as evidence. | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The symbol reference, type, pattern or glob the command asks about. Quote refs containing # or (). Empty only where the command allows it (ls, tree). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `limit` | integer | Maximum rows shown (default 3); the rest become a truncation footer. | | `in` | string | Only matches from artifacts whose label matches this glob. | | `exclude` | string | Skip matches from artifacts whose label matches this glob. | | `preferSources` | boolean | Rank callers from sources-paired artifacts first. | ## `jdx_diff` What changed between two artifacts? Public-API diff with named binary-compatibility rules (MEMBER_REMOVED, RETURN_TYPE_CHANGED, ...), most breaking first. `query` is the OLD artifact and `new` the candidate; each is a jar, class dir, glob, or Maven coordinate. Exits 0 whenever the comparison ran. | Parameter | Type | Description | |---|---|---| | `query` (required) | string | The OLD (baseline) artifact: jar file, class directory, glob, or group:artifact:version. | | `new` (required) | string | The NEW (candidate) artifact, compared against `query`. | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | | `visibility` | string | public (default: public and protected declarations) or all (every declared type and member). | | `includeSynthetic` | boolean | Compare bridge/synthetic members too, hidden by default. | | `severity` | string | How much to print: all (default), suspicious (breaking and suspicious), or breaking only. The tally always describes everything. | | `failOn` | string | none (default, always 0), breaking (exit 1 on a breaking change) or any (exit 1 on any difference). | | `limit` | integer | Maximum rows shown (default 200); the rest become a truncation footer. | ## `jdx_version` The jdx version this server was built as. | Parameter | Type | Description | |---|---|---| | `query` | string | Optional label echoed back in the envelope (empty is fine). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | ## `jdx_doctor` Environment report. The server refuses this over MCP — it describes the server's machine, not the client's — run `jdx doctor` in a shell instead. | Parameter | Type | Description | |---|---|---| | `query` | string | Optional label echoed back in the envelope (empty is fine). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | ## `jdx_health` Liveness probe: uptime, workspace served and queries answered. | Parameter | Type | Description | |---|---|---| | `query` | string | Optional label echoed back in the envelope (empty is fine). | | `workspace` | string | Named workspace to answer from (default: the workspace the server was started with). Stored workspaces only. | --- # jdx version IDE equivalent: Print the jdx version Print the jdx version. With --json, print it inside the standard JSON envelope. ## Usage ```text jdx version [] ``` ## Options | Option | Description | |---|---| | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | ## Full help text ```console $ jdx version --help Usage: jdx version [] Print the jdx version. With --json, print it inside the standard JSON envelope. Options: --json Emit machine-readable JSON instead of human-readable text (same information, D-007). -h, --help Show this message and exit ``` ## Learn more Guide: [Installation](https://mohammadmd1383.github.io/jdx/docs/installation/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx upgrade IDE equivalent: Self-update from GitHub Releases Self-update this jdx from GitHub Releases (latest, or --version TAG). Only works on release installs (install-release.sh); source builds are refused. --check only reports whether an update exists. Exits 1 when the tag has no release, 3 on a non-release install, 5 on download failure. ## Usage ```text jdx upgrade [] ``` ## Options | Option | Description | |---|---| | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--check` | Only report whether an update exists; never download or install. | | `--version=` | Upgrade (or check) a specific release tag, e.g. --version v1.1.0. Defaults to the latest release. | | `--repo=` | GitHub repo to upgrade from as OWNER/NAME (default MohammadMD1383/jdx). | | `-h, --help` | Show this message and exit | ## Full help text ```console $ jdx upgrade --help Usage: jdx upgrade [] Self-update this jdx from GitHub Releases (latest, or --version TAG). Only works on release installs (install-release.sh); source builds are refused. --check only reports whether an update exists. Exits 1 when the tag has no release, 3 on a non-release install, 5 on download failure. Options: --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --check Only report whether an update exists; never download or install. --version= Upgrade (or check) a specific release tag, e.g. --version v1.1.0. Defaults to the latest release. --repo= GitHub repo to upgrade from as OWNER/NAME (default MohammadMD1383/jdx). -h, --help Show this message and exit ``` ## Learn more Guide: [Installation](https://mohammadmd1383.github.io/jdx/docs/installation/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx doctor IDE equivalent: Self-diagnosis (JDK, index, AppCDS, daemon, workspace) Check the jdx environment and report one ok/warn/fail row per check (JDK, javap, cache, config, index, Kotlin module, AppCDS archive, daemon, workspace, agent setup). Exits 6 if any check fails. With --json, print the same report in the JSON envelope. ## Usage ```text jdx doctor [] ``` ## Options | Option | Description | |---|---| | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | ## Full help text ```console $ jdx doctor --help Usage: jdx doctor [] Check the jdx environment and report one ok/warn/fail row per check (JDK, javap, cache, config, index, Kotlin module, AppCDS archive, daemon, workspace, agent setup). Exits 6 if any check fails. With --json, print the same report in the JSON envelope. Options: --json Emit machine-readable JSON instead of human-readable text (same information, D-007). -h, --help Show this message and exit ``` ## Learn more Guide: [Installation](https://mohammadmd1383.github.io/jdx/docs/installation/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx show IDE equivalent: Go to declaration Show the class card for a type: kind, modifiers, supertypes, member counts, provenance and the next command to run. Takes a type reference (e.g. 'com.example.Point', 'Gson', 'java.util.Map$Entry'); member references are a usage error. Reads --jars roots plus the selected workspace (-w, JDX_WORKSPACE, jdx ws use) plus the JDK stdlib unless --no-jdk. With no workspace selected, Gradle/Maven project roots auto-discover from the working directory. Exits 1 when the type is unknown, 2 when a short name is ambiguous, 4 when the workspace cannot be resolved. ## Usage ```text jdx show [] ``` ## Example ```console $ jdx show com.google.gson.Gson --coord com.google.code.gson:gson:2.14.0 class com.google.gson.Gson public final class Gson extends java.lang.Object members: 2 constructors, 33 methods, 26 fields warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) source: gson-2.14.0.jar (bytecode) next: jdx members com.google.gson.Gson --inherited ``` ## Options | Option | Description | |---|---| | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Type reference to show (full, short or nested form). | ## Full help text ```console $ jdx show --help Usage: jdx show [] Show the class card for a type: kind, modifiers, supertypes, member counts, provenance and the next command to run. Takes a type reference (e.g. 'com.example.Point', 'Gson', 'java.util.Map$Entry'); member references are a usage error. Reads --jars roots plus the selected workspace (-w, JDX_WORKSPACE, jdx ws use) plus the JDK stdlib unless --no-jdk. With no workspace selected, Gradle/Maven project roots auto-discover from the working directory. Exits 1 when the type is unknown, 2 when a short name is ambiguous, 4 when the workspace cannot be resolved. Options: --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Type reference to show (full, short or nested form). ``` ## Learn more Guide: [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx members IDE equivalent: Code completion after `.` List the members of a type — the '.' completion equivalent. Inherited members are included by default (--inherited), grouped by declaring type; pass --declared for only the type's own members. java.lang.Object members collapse to one line unless --from java.lang.Object expands them. Exits 1 when the type is unknown, 2 when a short name is ambiguous. ## Usage ```text jdx members [] ``` ## Example ```console $ jdx members com.google.gson.JsonArray --declared --limit 12 --coord com.google.code.gson:gson:2.14.0 members of com.google.gson.JsonArray constructors declared on com.google.gson.JsonArray: constructor public JsonArray() constructor public JsonArray(int capacity) methods declared on com.google.gson.JsonArray: method public boolean contains(com.google.gson.JsonElement element) method public boolean equals(java.lang.Object o) method public boolean getAsBoolean() method public boolean isEmpty() method public boolean remove(com.google.gson.JsonElement element) method public byte getAsByte() method public char getAsCharacter() method public com.google.gson.JsonArray deepCopy() method public com.google.gson.JsonElement get(int i) method public com.google.gson.JsonElement remove(int index) 12 of 32 members shown (--limit 32 to see more) warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) source: gson-2.14.0.jar (bytecode) ``` ## Options | Option | Description | |---|---| | `--declared` | Only members declared on this exact type (overrides the --inherited default). | | `--inherited` | Include inherited members (the default; accepted for explicitness). | | `--kind=(all\|method\|field\|ctor\|property)` | Member kind to list: method, field, ctor, property or all. | | `--access=(public\|protected\|package\|private\|all)` | Visibility to list: public, protected, package, private or all (default: public + protected). | | `--static` | Only static members (mutually exclusive with --instance). | | `--instance` | Only instance members (mutually exclusive with --static). | | `--from=` | Only members inherited from a specific supertype (e.g. --from java.lang.Object). | | `--grep=` | Only members whose name matches this regex. | | `--include-synthetic` | Include bridge/synthetic members, hidden by default. | | `--limit=` | Maximum member rows shown (default 50); the rest become a truncation footer. | | `--with-doc` | Include the first javadoc sentence per member. | | `--brief` | Minimal text output: bare member rows with no group headers and no provenance block (warnings and footers stay). Mutually exclusive with --with-doc. Text only; --json is unaffected. | | `--max-lines=` | Maximum text lines shown; the rest become a continuation footer. Text only; --json is unaffected. | | `--sort=(kind\|name\|declaring)` | Row order: kind (the default, kind-then-name in linearisation order); name (flat name-first across kinds and groups); declaring (alphabetical by declaring type). | | `--view=(kotlin\|jvm)` | Declaration projection: kotlin (the default, true Kotlin declarations from @Metadata) or jvm (the raw JVM projection: JVM names, hidden Continuation params, getters/setters, mangled internal names). | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Type reference to list members of. | ## Full help text ```console $ jdx members --help Usage: jdx members [] List the members of a type — the '.' completion equivalent. Inherited members are included by default (--inherited), grouped by declaring type; pass --declared for only the type's own members. java.lang.Object members collapse to one line unless --from java.lang.Object expands them. Exits 1 when the type is unknown, 2 when a short name is ambiguous. Options: --declared Only members declared on this exact type (overrides the --inherited default). --inherited Include inherited members (the default; accepted for explicitness). --kind=(all|method|field|ctor|property) Member kind to list: method, field, ctor, property or all. --access=(public|protected|package|private|all) Visibility to list: public, protected, package, private or all (default: public + protected). --static Only static members (mutually exclusive with --instance). --instance Only instance members (mutually exclusive with --static). --from= Only members inherited from a specific supertype (e.g. --from java.lang.Object). --grep= Only members whose name matches this regex. --include-synthetic Include bridge/synthetic members, hidden by default. --limit= Maximum member rows shown (default 50); the rest become a truncation footer. --with-doc Include the first javadoc sentence per member. --brief Minimal text output: bare member rows with no group headers and no provenance block (warnings and footers stay). Mutually exclusive with --with-doc. Text only; --json is unaffected. --max-lines= Maximum text lines shown; the rest become a continuation footer. Text only; --json is unaffected. --sort=(kind|name|declaring) Row order: kind (the default, kind-then-name in linearisation order); name (flat name-first across kinds and groups); declaring (alphabetical by declaring type). --view=(kotlin|jvm) Declaration projection: kotlin (the default, true Kotlin declarations from @Metadata) or jvm (the raw JVM projection: JVM names, hidden Continuation params, getters/setters, mangled internal names). --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Type reference to list members of. ``` ## Learn more Guide: [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx outline IDE equivalent: File structure Outline a type: one dense line per member declared on it (never inherited members). Same filters as members --declared. Exits 1 when the type is unknown, 2 when a short name is ambiguous. ## Usage ```text jdx outline [] ``` ## Example ```console $ jdx outline com.google.gson.JsonPrimitive --coord com.google.code.gson:gson:2.14.0 members of com.google.gson.JsonPrimitive constructors declared on com.google.gson.JsonPrimitive: constructor public JsonPrimitive(java.lang.Boolean bool) constructor public JsonPrimitive(java.lang.Character c) constructor public JsonPrimitive(java.lang.Number number) constructor public JsonPrimitive(java.lang.String string) methods declared on com.google.gson.JsonPrimitive: method public boolean equals(java.lang.Object obj) method public boolean getAsBoolean() method public boolean isBoolean() method public boolean isNumber() method public boolean isString() method public byte getAsByte() method public char getAsCharacter() method public com.google.gson.JsonPrimitive deepCopy() method public double getAsDouble() method public float getAsFloat() method public int getAsInt() method public int hashCode() method public java.lang.Number getAsNumber() method public java.lang.String getAsString() method public java.math.BigDecimal getAsBigDecimal() method public java.math.BigInteger getAsBigInteger() method public long getAsLong() method public short getAsShort() warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) source: gson-2.14.0.jar (bytecode) ``` ## Options | Option | Description | |---|---| | `--kind=(all\|method\|field\|ctor\|property)` | Member kind to list: method, field, ctor, property or all. | | `--access=(public\|protected\|package\|private\|all)` | Visibility to list: public, protected, package, private or all (default: public + protected). | | `--static` | Only static members (mutually exclusive with --instance). | | `--instance` | Only instance members (mutually exclusive with --static). | | `--from=` | Only members inherited from a specific supertype (rarely useful with outline). | | `--grep=` | Only members whose name matches this regex. | | `--include-synthetic` | Include bridge/synthetic members, hidden by default. | | `--limit=` | Maximum member rows shown (default 50); the rest become a truncation footer. | | `--with-doc` | Include the first javadoc sentence per member. | | `--brief` | Minimal text output: bare member rows with no group headers and no provenance block (warnings and footers stay). Mutually exclusive with --with-doc. Text only; --json is unaffected. | | `--max-lines=` | Maximum text lines shown; the rest become a continuation footer. Text only; --json is unaffected. | | `--sort=(kind\|name\|declaring)` | Row order: kind (the default, kind-then-name in linearisation order); name (flat name-first across kinds and groups); declaring (alphabetical by declaring type). | | `--view=(kotlin\|jvm)` | Declaration projection: kotlin (the default, true Kotlin declarations from @Metadata) or jvm (the raw JVM projection: JVM names, hidden Continuation params, getters/setters, mangled internal names). | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Type reference to outline. | ## Full help text ```console $ jdx outline --help Usage: jdx outline [] Outline a type: one dense line per member declared on it (never inherited members). Same filters as members --declared. Exits 1 when the type is unknown, 2 when a short name is ambiguous. Options: --kind=(all|method|field|ctor|property) Member kind to list: method, field, ctor, property or all. --access=(public|protected|package|private|all) Visibility to list: public, protected, package, private or all (default: public + protected). --static Only static members (mutually exclusive with --instance). --instance Only instance members (mutually exclusive with --static). --from= Only members inherited from a specific supertype (rarely useful with outline). --grep= Only members whose name matches this regex. --include-synthetic Include bridge/synthetic members, hidden by default. --limit= Maximum member rows shown (default 50); the rest become a truncation footer. --with-doc Include the first javadoc sentence per member. --brief Minimal text output: bare member rows with no group headers and no provenance block (warnings and footers stay). Mutually exclusive with --with-doc. Text only; --json is unaffected. --max-lines= Maximum text lines shown; the rest become a continuation footer. Text only; --json is unaffected. --sort=(kind|name|declaring) Row order: kind (the default, kind-then-name in linearisation order); name (flat name-first across kinds and groups); declaring (alphabetical by declaring type). --view=(kotlin|jvm) Declaration projection: kotlin (the default, true Kotlin declarations from @Metadata) or jvm (the raw JVM projection: JVM names, hidden Continuation params, getters/setters, mangled internal names). --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Type reference to outline. ``` ## Learn more Guide: [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx body IDE equivalent: Open the method Show the body of a member: the verbatim source slice with 1-based line numbers plus provenance (artifact, file, lines). Takes a member reference (e.g. 'com.example.Point#getX()', "Gson#toJson(Object)"). An under-specified name with several overloads exits 2 with candidates. Without paired sources the query names the decompiler tasks instead of guessing. Exits 1 when the member or its sources are unknown, 2 when ambiguous, 4 when the workspace cannot be resolved. ## Usage ```text jdx body [] ``` ## Example ```console $ jdx body 'com.google.gson.Gson#toJson(Object)' --coord com.google.code.gson:gson:2.14.0 com.google.gson.Gson#toJson(java.lang.Object) source: gson-2.14.0-sources.jar · com/google/gson/Gson.java:542-547 public String toJson(Object src) { if (src == null) { return toJson(JsonNull.INSTANCE); } return toJson(src, src.getClass()); } warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) next: jdx show com.google.gson.Gson ``` ## Options | Option | Description | |---|---| | `--context=` | Surrounding source lines shown each side of the member (default 0). | | `--line-numbers` | Prefix each shown line with its 1-based number. | | `--max-lines=` | Maximum body lines shown (default 200); the rest become a truncation footer. | | `--engine=(vineflower\|javap)` | Disassembly engine: vineflower forces reconstruction even when sources are paired (default serves sources, falling back to vineflower); javap shows the raw bytecode instead. | | `--with-doc` | Include the member's javadoc beside the body. | | `--with-signature` | Prepend the resolved bytecode signature header to the body. | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Member reference to show the body of (full or short form). | ## Full help text ```console $ jdx body --help Usage: jdx body [] Show the body of a member: the verbatim source slice with 1-based line numbers plus provenance (artifact, file, lines). Takes a member reference (e.g. 'com.example.Point#getX()', "Gson#toJson(Object)"). An under-specified name with several overloads exits 2 with candidates. Without paired sources the query names the decompiler tasks instead of guessing. Exits 1 when the member or its sources are unknown, 2 when ambiguous, 4 when the workspace cannot be resolved. Options: --context= Surrounding source lines shown each side of the member (default 0). --line-numbers Prefix each shown line with its 1-based number. --max-lines= Maximum body lines shown (default 200); the rest become a truncation footer. --engine=(vineflower|javap) Disassembly engine: vineflower forces reconstruction even when sources are paired (default serves sources, falling back to vineflower); javap shows the raw bytecode instead. --with-doc Include the member's javadoc beside the body. --with-signature Prepend the resolved bytecode signature header to the body. --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Member reference to show the body of (full or short form). ``` ## Learn more Guide: [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx source IDE equivalent: Open the file / decompile Show a type's source: the verbatim file with 1-based line numbers plus provenance (artifact, file, lines). Takes a type reference (e.g. 'com.example.Point', 'Gson'). Slice it with --lines A:B or center on a member with --around '#' --context N. Without paired sources the query names the decompiler tasks instead of guessing. Exits 1 when the type or its sources are unknown, 2 when ambiguous, 4 when the workspace cannot be resolved. ## Usage ```text jdx source [] ``` ## Example ```console $ jdx source com.google.gson.JsonNull --lines 17:45 --coord com.google.code.gson:gson:2.14.0 com.google.gson.JsonNull source: gson-2.14.0-sources.jar · com/google/gson/JsonNull.java:17-45 package com.google.gson; /** * A class representing a JSON {@code null} value. * * @author Inderjeet Singh * @author Joel Leitch * @since 1.2 */ public final class JsonNull extends JsonElement { /** * Singleton for {@code JsonNull}. * * @since 1.8 */ public static final JsonNull INSTANCE = new JsonNull(); /** * Creates a new {@code JsonNull} object. * * @deprecated Deprecated since Gson version 1.8, use {@link #INSTANCE} instead. */ @Deprecated public JsonNull() { // Do nothing } /** … 3 more lines (run it yourself to see them all) ``` ## Options | Option | Description | |---|---| | `--lines=` | Show only lines A:B (1-based inclusive, e.g. --lines 100:180). Mutually exclusive with --around. | | `--around=` | Center the slice on a member reference (e.g. --around 'Point#getX()'); --context N expands it each side. | | `--context=` | Surrounding source lines shown each side of the --around member (default 0). | | `--line-numbers` | Prefix each shown line with its 1-based number. | | `--max-lines=` | Maximum source lines shown (default 200); the rest become a truncation footer. | | `--engine=(vineflower\|javap)` | Disassembly engine: vineflower forces reconstruction even when sources are paired (default serves sources, falling back to vineflower); javap shows the raw bytecode instead. | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Type reference to show the source of (full or short form). | ## Full help text ```console $ jdx source --help Usage: jdx source [] Show a type's source: the verbatim file with 1-based line numbers plus provenance (artifact, file, lines). Takes a type reference (e.g. 'com.example.Point', 'Gson'). Slice it with --lines A:B or center on a member with --around '#' --context N. Without paired sources the query names the decompiler tasks instead of guessing. Exits 1 when the type or its sources are unknown, 2 when ambiguous, 4 when the workspace cannot be resolved. Options: --lines= Show only lines A:B (1-based inclusive, e.g. --lines 100:180). Mutually exclusive with --around. --around= Center the slice on a member reference (e.g. --around 'Point#getX()'); --context N expands it each side. --context= Surrounding source lines shown each side of the --around member (default 0). --line-numbers Prefix each shown line with its 1-based number. --max-lines= Maximum source lines shown (default 200); the rest become a truncation footer. --engine=(vineflower|javap) Disassembly engine: vineflower forces reconstruction even when sources are paired (default serves sources, falling back to vineflower); javap shows the raw bytecode instead. --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Type reference to show the source of (full or short form). ``` ## Learn more Guide: [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx signature IDE equivalent: Parameter info Show the signature of a member: one source-style line per overload with real parameter names, generics, throws and defaults, plus provenance. Takes a member reference (e.g. 'com.example.Point#getX()', "Gson#toJson(Object)"). An under-specified name lists every overload. Exits 1 when the member is unknown, 3 on a type or invalid reference, 4 when the workspace cannot be resolved. ## Usage ```text jdx signature [] ``` ## Example ```console $ jdx signature 'com.google.gson.Gson#fromJson' --limit 5 --coord com.google.code.gson:gson:2.14.0 signatures of com.google.gson.Gson#fromJson public T fromJson(java.lang.String json, java.lang.Class classOfT) throws com.google.gson.JsonSyntaxException public T fromJson(java.lang.String json, java.lang.reflect.Type typeOfT) throws com.google.gson.JsonSyntaxException public T fromJson(java.lang.String json, com.google.gson.reflect.TypeToken typeOfT) throws com.google.gson.JsonSyntaxException public T fromJson(java.io.Reader json, java.lang.Class classOfT) throws com.google.gson.JsonSyntaxException, com.google.gson.JsonIOException public T fromJson(java.io.Reader json, java.lang.reflect.Type typeOfT) throws com.google.gson.JsonIOException, com.google.gson.JsonSyntaxException 5 of 11 signatures shown (--limit 11 to see more) warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) source: gson-2.14.0.jar (bytecode) next: jdx show com.google.gson.Gson ``` ## Options | Option | Description | |---|---| | `--include-synthetic` | Show bridge/synthetic members, hidden by default. | | `--view=(kotlin\|jvm)` | Declaration projection: kotlin (the default, true Kotlin declarations from @Metadata) or jvm (the raw JVM projection: JVM names, hidden Continuation params, getters/setters, mangled internal names). | | `--limit=` | Maximum signatures shown (default 50); the rest become a truncation footer. | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Member reference to show the signature of (full or short form). | ## Full help text ```console $ jdx signature --help Usage: jdx signature [] Show the signature of a member: one source-style line per overload with real parameter names, generics, throws and defaults, plus provenance. Takes a member reference (e.g. 'com.example.Point#getX()', "Gson#toJson(Object)"). An under-specified name lists every overload. Exits 1 when the member is unknown, 3 on a type or invalid reference, 4 when the workspace cannot be resolved. Options: --include-synthetic Show bridge/synthetic members, hidden by default. --view=(kotlin|jvm) Declaration projection: kotlin (the default, true Kotlin declarations from @Metadata) or jvm (the raw JVM projection: JVM names, hidden Continuation params, getters/setters, mangled internal names). --limit= Maximum signatures shown (default 50); the rest become a truncation footer. --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Member reference to show the signature of (full or short form). ``` ## Learn more Guide: [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx doc IDE equivalent: Quick documentation Show the javadoc of a type or member: rendered plain text (HTML stripped, inline tags unwrapped, block tags kept) plus provenance (artifact, file, lines). Takes a type or member reference (e.g. 'com.example.Foo', "Gson#toJson(Object)"). Undocumented methods fall back to the nearest documenting supertype unless --no-inherited. Exits 1 when the symbol or its documentation is unknown, 2 when a member reference is ambiguous, 4 when the workspace cannot be resolved. ## Usage ```text jdx doc [] ``` ## Example ```console $ jdx doc 'com.google.gson.Gson#toJson(Object)' --coord com.google.code.gson:gson:2.14.0 com.google.gson.Gson#toJson(java.lang.Object) source: gson-2.14.0-sources.jar · com/google/gson/Gson.java:527-541 This method serializes the specified object into its equivalent JSON representation. This method should be used when the specified object is not a generic type. This method uses {@link Class#getClass()} to get the type for the specified object, but the getClass() loses the generic type information because of the Type Erasure feature of Java. Note that this method works fine if any of the object fields are of generic type, just the object itself should not be of a generic type. If the object is of generic type, use Type) instead. If you want to write out the object to a Writer, use {@link #toJson(Object, Appendable)} instead. @param src the object for which JSON representation is to be created @return JSON representation of src. @see #toJson(Object, Appendable) @see #toJson(Object, Type) warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) next: jdx show com.google.gson.Gson ``` ## Options | Option | Description | |---|---| | `--inherited` | Pull documentation from the nearest documenting supertype (the default; accepted for explicitness). | | `--no-inherited` | Only the symbol's own documentation (mutually exclusive with --inherited). | | `--raw` | Serve the verbatim comment instead of rendered plain text. | | `--max-lines=` | Maximum doc lines shown (default 200); the rest become a truncation footer. | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Type or member reference to show the documentation of (full or short form). | ## Full help text ```console $ jdx doc --help Usage: jdx doc [] Show the javadoc of a type or member: rendered plain text (HTML stripped, inline tags unwrapped, block tags kept) plus provenance (artifact, file, lines). Takes a type or member reference (e.g. 'com.example.Foo', "Gson#toJson(Object)"). Undocumented methods fall back to the nearest documenting supertype unless --no-inherited. Exits 1 when the symbol or its documentation is unknown, 2 when a member reference is ambiguous, 4 when the workspace cannot be resolved. Options: --inherited Pull documentation from the nearest documenting supertype (the default; accepted for explicitness). --no-inherited Only the symbol's own documentation (mutually exclusive with --inherited). --raw Serve the verbatim comment instead of rendered plain text. --max-lines= Maximum doc lines shown (default 200); the rest become a truncation footer. --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Type or member reference to show the documentation of (full or short form). ``` ## Learn more Guide: [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx search IDE equivalent: Search everywhere Search symbols across the workspace: types, members, packages and JDK modules. Glob by default ('*Http*Client'), --regex for regexes, bare words match case-insensitively or as camel humps (HMap finds HashMap). --kind narrows to class|interface|enum|record|annotation|method|field|package|module (default: types, packages and modules; pass method|field to scan members). --fuzzy retries a miss by Levenshtein distance. Exits 1 when nothing matches. ## Usage ```text jdx search [] ``` ## Example ```console $ jdx search '*TypeAdapter*' --kind class --limit 10 --coord com.google.code.gson:gson:2.14.0 search results for '*TypeAdapter*' class com.google.gson.Gson$FutureTypeAdapter gson-2.14.0.jar class com.google.gson.TypeAdapter gson-2.14.0.jar class com.google.gson.TypeAdapter$1 gson-2.14.0.jar class com.google.gson.TypeAdapter$NullSafeTypeAdapter gson-2.14.0.jar class com.google.gson.internal.bind.ArrayTypeAdapter gson-2.14.0.jar class com.google.gson.internal.bind.ArrayTypeAdapter$1 gson-2.14.0.jar class com.google.gson.internal.bind.CollectionTypeAdapterFactory gson-2.14.0.jar class com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter gson-2.14.0.jar class com.google.gson.internal.bind.DefaultDateTypeAdapter gson-2.14.0.jar class com.google.gson.internal.bind.DefaultDateTypeAdapter$1 gson-2.14.0.jar 10 of 101 results shown (--limit 101 to see more) warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` ## Options | Option | Description | |---|---| | `--kind=(all\|type\|class\|interface\|enum\|record\|annotation\|object\|companion\|method\|field\|package\|module)` | Symbol kind to search: all, type, class, interface, enum, record, annotation, method, field, package or module. | | `--regex` | Treat the pattern as a regex instead of a glob. | | `--fuzzy` | Levenshtein fallback: when nothing matches, retry within two edits. | | `--in=` | Only artifacts whose label matches this glob (jar file name or JDK module). | | `--package=` | Only symbols in packages matching this glob. | | `--limit=` | Maximum hits shown (default 50); the rest become a truncation footer. | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Search pattern: glob, --regex regex, or bare word. | ## Full help text ```console $ jdx search --help Usage: jdx search [] Search symbols across the workspace: types, members, packages and JDK modules. Glob by default ('*Http*Client'), --regex for regexes, bare words match case-insensitively or as camel humps (HMap finds HashMap). --kind narrows to class|interface|enum|record|annotation|method|field|package|module (default: types, packages and modules; pass method|field to scan members). --fuzzy retries a miss by Levenshtein distance. Exits 1 when nothing matches. Options: --kind=(all|type|class|interface|enum|record|annotation|object|companion|method|field|package|module) Symbol kind to search: all, type, class, interface, enum, record, annotation, method, field, package or module. --regex Treat the pattern as a regex instead of a glob. --fuzzy Levenshtein fallback: when nothing matches, retry within two edits. --in= Only artifacts whose label matches this glob (jar file name or JDK module). --package= Only symbols in packages matching this glob. --limit= Maximum hits shown (default 50); the rest become a truncation footer. --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Search pattern: glob, --regex regex, or bare word. ``` ## Learn more Guide: [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx resolve IDE equivalent: What is this symbol? Resolve an unqualified or partial name to every candidate, each with kind and artifact — the disambiguator for unknown symbols in stack traces and snippets. Accepts 'Gson', 'com.google.gson.Gson', 'Gson#toJson' and package names. Several candidates are success (exit 0); none is exit 1 with did-you-mean. ## Usage ```text jdx resolve [] ``` ## Example ```console $ jdx resolve JsonParser --coord com.google.code.gson:gson:2.14.0 search results for 'JsonParser' class com.google.gson.JsonParser gson-2.14.0.jar warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` ## Options | Option | Description | |---|---| | `--limit=` | Maximum candidates shown (default 50); the rest become a truncation footer. | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Type, member (Type#member) or package name to resolve. | ## Full help text ```console $ jdx resolve --help Usage: jdx resolve [] Resolve an unqualified or partial name to every candidate, each with kind and artifact — the disambiguator for unknown symbols in stack traces and snippets. Accepts 'Gson', 'com.google.gson.Gson', 'Gson#toJson' and package names. Several candidates are success (exit 0); none is exit 1 with did-you-mean. Options: --limit= Maximum candidates shown (default 50); the rest become a truncation footer. --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Type, member (Type#member) or package name to resolve. ``` ## Learn more Guide: [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx ls IDE equivalent: External library browser (packages/types) List packages in the workspace ('com.google.*') with type counts; an exact package name ('com.google.gson') lists the types inside it. Exits 1 when nothing matches. ## Usage ```text jdx ls [] [] ``` ## Example ```console $ jdx ls com.google.gson.stream --coord com.google.code.gson:gson:2.14.0 types in package 'com.google.gson.stream' package com.google.gson.stream (7 types) class com.google.gson.stream.JsonReader gson-2.14.0.jar class com.google.gson.stream.JsonReader$1 gson-2.14.0.jar class com.google.gson.stream.JsonScope gson-2.14.0.jar enum com.google.gson.stream.JsonToken gson-2.14.0.jar class com.google.gson.stream.JsonWriter gson-2.14.0.jar class com.google.gson.stream.MalformedJsonException gson-2.14.0.jar interface com.google.gson.stream.package-info gson-2.14.0.jar warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` ## Options | Option | Description | |---|---| | `--limit=` | Maximum rows shown (default 50); the rest become a truncation footer. | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Package glob (default '*' — every package), or one exact package to list types for. | ## Full help text ```console $ jdx ls --help Usage: jdx ls [] [] List packages in the workspace ('com.google.*') with type counts; an exact package name ('com.google.gson') lists the types inside it. Exits 1 when nothing matches. Options: --limit= Maximum rows shown (default 50); the rest become a truncation footer. --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Package glob (default '*' — every package), or one exact package to list types for. ``` ## Learn more Guide: [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx tree IDE equivalent: External library browser (artifacts) Show the package forest of each matching artifact (jar file name or JDK module; default '*' — every artifact). --depth caps nesting (0 shows top segments only), --counts adds per-package subtree type counts. Exits 1 when no artifact matches. ## Usage ```text jdx tree [] [] ``` ## Example ```console $ jdx tree 'gson*' --depth 3 --coord com.google.code.gson:gson:2.14.0 package tree for 'gson*' gson-2.14.0.jar com com.google com.google.gson com.google.gson.annotations com.google.gson.internal com.google.gson.reflect com.google.gson.stream warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` ## Options | Option | Description | |---|---| | `--depth=` | Maximum package nesting shown (default 8; 0 shows top-level segments only). | | `--counts` | Show per-package subtree type counts. | | `--limit=` | Maximum package nodes shown (default 50); the rest become a truncation footer. | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Artifact glob: jar file name or JDK module (default '*' — every artifact). | ## Full help text ```console $ jdx tree --help Usage: jdx tree [] [] Show the package forest of each matching artifact (jar file name or JDK module; default '*' — every artifact). --depth caps nesting (0 shows top segments only), --counts adds per-package subtree type counts. Exits 1 when no artifact matches. Options: --depth= Maximum package nesting shown (default 8; 0 shows top-level segments only). --counts Show per-package subtree type counts. --limit= Maximum package nodes shown (default 50); the rest become a truncation footer. --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Artifact glob: jar file name or JDK module (default '*' — every artifact). ``` ## Learn more Guide: [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx usages IDE equivalent: Find usages Find usages of a type or member across the workspace's bytecode roots plus project source dirs: one row per referencing method (bytecode) or mentioning line (source dirs, ref kind). --kind narrows to call|read|write|ref|new|throw|annotation (default: all); new shows constructor call sites, throw shows methods declaring the type in throws (declaration-level, not ATHROW throw sites), annotation shows annotated classes and members; source-dir hits are textual mentions (ref only), so call|read|write|new|throw|annotation show bytecode-backed edges alone. impl|override land with hierarchy (jdx hierarchy, jdx implementors). --in/--exclude filter by artifact label (jar file, JDK module or source-dir name). --src adds a source dir root (repeatable; stored via jdx ws create --src). Exits 1 when the symbol is unknown or unused, 2 on an ambiguous short name. ## Usage ```text jdx usages [] ``` ## Example ```console $ jdx usages com.google.gson.JsonNull --limit 10 --coord com.google.code.gson:gson:2.14.0 usages of 'com.google.gson.JsonNull' gson-2.14.0.jar (10) read com.google.gson.Gson#toJson(java.lang.Object) -> #INSTANCE read com.google.gson.Gson#toJson(java.lang.Object, java.lang.Appendable) -> #INSTANCE read com.google.gson.Gson#toJsonTree(java.lang.Object) -> #INSTANCE read com.google.gson.JsonArray#add(com.google.gson.JsonElement) -> #INSTANCE read com.google.gson.JsonArray#add(java.lang.Boolean) -> #INSTANCE read com.google.gson.JsonArray#add(java.lang.Character) -> #INSTANCE read com.google.gson.JsonArray#add(java.lang.Number) -> #INSTANCE read com.google.gson.JsonArray#add(java.lang.String) -> #INSTANCE read com.google.gson.JsonArray#set(int, com.google.gson.JsonElement) -> #INSTANCE ref com.google.gson.JsonElement#getAsJsonNull() -> com.google.gson.JsonNull 10 of 30 usages shown (--limit 30 to see more) warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` ## Options | Option | Description | |---|---| | `--kind=(all\|call\|read\|write\|ref\|impl\|override\|new\|throw\|annotation)` | Edge kind: call, read, write, ref, new, throw, annotation or all (default all). throw = throws declarations, not ATHROW sites. impl\|override land with hierarchy (jdx hierarchy, jdx implementors). | | `--in=` | Only artifacts whose label matches this glob (jar file name, JDK module or source-dir name). | | `--exclude=` | Skip artifacts whose label matches this glob. | | `--limit=` | Maximum usages shown (default 50); the rest become a truncation footer. | | `--context=` | Source lines around each call site (not supported: source-rendered call sites live in jdx samples). | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `--src=` | Source-dir roots: directories of .java/.kt files scanned textually for whole-word mentions (ref kind, from :). Merge in front of the selected workspace's stored srcs. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Type or member reference to find usages of (full or short form). | ## Full help text ```console $ jdx usages --help Usage: jdx usages [] Find usages of a type or member across the workspace's bytecode roots plus project source dirs: one row per referencing method (bytecode) or mentioning line (source dirs, ref kind). --kind narrows to call|read|write|ref|new|throw|annotation (default: all); new shows constructor call sites, throw shows methods declaring the type in throws (declaration-level, not ATHROW throw sites), annotation shows annotated classes and members; source-dir hits are textual mentions (ref only), so call|read|write|new|throw|annotation show bytecode-backed edges alone. impl|override land with hierarchy (jdx hierarchy, jdx implementors). --in/--exclude filter by artifact label (jar file, JDK module or source-dir name). --src adds a source dir root (repeatable; stored via jdx ws create --src). Exits 1 when the symbol is unknown or unused, 2 on an ambiguous short name. Options: --kind=(all|call|read|write|ref|impl|override|new|throw|annotation) Edge kind: call, read, write, ref, new, throw, annotation or all (default all). throw = throws declarations, not ATHROW sites. impl|override land with hierarchy (jdx hierarchy, jdx implementors). --in= Only artifacts whose label matches this glob (jar file name, JDK module or source-dir name). --exclude= Skip artifacts whose label matches this glob. --limit= Maximum usages shown (default 50); the rest become a truncation footer. --context= Source lines around each call site (not supported: source-rendered call sites live in jdx samples). --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. --src= Source-dir roots: directories of .java/.kt files scanned textually for whole-word mentions (ref kind, from :). Merge in front of the selected workspace's stored srcs. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Type or member reference to find usages of (full or short form). ``` ## Learn more Guide: [Usages, hierarchy & calls](https://mohammadmd1383.github.io/jdx/docs/navigating-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx hierarchy IDE equivalent: Type hierarchy Show a type's hierarchy: supertypes upward (full transitive chain, interfaces included) and subtypes/implementors downward across the workspace. --up/--down select directions (default both), --direct shows one level, --depth N caps transitive levels, --in/--exclude filter subtypes by artifact label. Member refs exit 3 (takes a type); unknown types exit 1, ambiguous short names exit 2. ## Usage ```text jdx hierarchy [] ``` ## Example ```console $ jdx hierarchy com.google.gson.JsonElement --coord com.google.code.gson:gson:2.14.0 hierarchy of 'com.google.gson.JsonElement' supertypes extends java.lang.Object java.base subtypes in workspace (4) class com.google.gson.JsonArray gson-2.14.0.jar class com.google.gson.JsonNull gson-2.14.0.jar class com.google.gson.JsonObject gson-2.14.0.jar class com.google.gson.JsonPrimitive gson-2.14.0.jar warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` ## Options | Option | Description | |---|---| | `--up` | Show supertypes upward (default when neither --up nor --down is given). | | `--down` | Show subtypes downward (default when neither --up nor --down is given). | | `--direct` | Show one level only (direct supertypes and direct subtypes). | | `--depth=` | Maximum transitive levels shown (default unlimited). | | `--in=` | Only subtypes whose artifact label matches this glob (jar file name, JDK module or class-dir name). | | `--exclude=` | Skip subtypes whose artifact label matches this glob. | | `--limit=` | Maximum subtypes shown (default 50); the rest become a truncation footer. | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Type reference to show the hierarchy of (full or short form). | ## Full help text ```console $ jdx hierarchy --help Usage: jdx hierarchy [] Show a type's hierarchy: supertypes upward (full transitive chain, interfaces included) and subtypes/implementors downward across the workspace. --up/--down select directions (default both), --direct shows one level, --depth N caps transitive levels, --in/--exclude filter subtypes by artifact label. Member refs exit 3 (takes a type); unknown types exit 1, ambiguous short names exit 2. Options: --up Show supertypes upward (default when neither --up nor --down is given). --down Show subtypes downward (default when neither --up nor --down is given). --direct Show one level only (direct supertypes and direct subtypes). --depth= Maximum transitive levels shown (default unlimited). --in= Only subtypes whose artifact label matches this glob (jar file name, JDK module or class-dir name). --exclude= Skip subtypes whose artifact label matches this glob. --limit= Maximum subtypes shown (default 50); the rest become a truncation footer. --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Type reference to show the hierarchy of (full or short form). ``` ## Learn more Guide: [Usages, hierarchy & calls](https://mohammadmd1383.github.io/jdx/docs/navigating-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx implementors IDE equivalent: Who implements this? List a type's implementors/subtypes across the workspace (alias for `hierarchy --down`). --direct shows direct subtypes only, --depth N caps transitive levels, --in/--exclude filter by artifact label. ## Usage ```text jdx implementors [] ``` ## Example ```console $ jdx implementors com.google.gson.TypeAdapterFactory --direct --limit 10 --coord com.google.code.gson:gson:2.14.0 hierarchy of 'com.google.gson.TypeAdapterFactory' subtypes in workspace (10) class com.google.gson.internal.Excluder gson-2.14.0.jar class com.google.gson.internal.bind.ArrayTypeAdapter$1 gson-2.14.0.jar class com.google.gson.internal.bind.CollectionTypeAdapterFactory gson-2.14.0.jar class com.google.gson.internal.bind.DefaultDateTypeAdapter$1 gson-2.14.0.jar class com.google.gson.internal.bind.EnumTypeAdapter$1 gson-2.14.0.jar class com.google.gson.internal.bind.JavaTimeTypeAdapters$14 gson-2.14.0.jar class com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory gson-2.14.0.jar class com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory$DummyTypeAdapterFactory gson-2.14.0.jar class com.google.gson.internal.bind.MapTypeAdapterFactory gson-2.14.0.jar class com.google.gson.internal.bind.NumberTypeAdapter$1 gson-2.14.0.jar 10 of 21 subtypes shown (--limit 21 to see more) warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` ## Options | Option | Description | |---|---| | `--direct` | Show direct subtypes only. | | `--depth=` | Maximum transitive levels shown (default unlimited). | | `--in=` | Only subtypes whose artifact label matches this glob (jar file name, JDK module or class-dir name). | | `--exclude=` | Skip subtypes whose artifact label matches this glob. | | `--limit=` | Maximum subtypes shown (default 50); the rest become a truncation footer. | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Type reference to list implementors of (full or short form). | ## Full help text ```console $ jdx implementors --help Usage: jdx implementors [] List a type's implementors/subtypes across the workspace (alias for `hierarchy --down`). --direct shows direct subtypes only, --depth N caps transitive levels, --in/--exclude filter by artifact label. Options: --direct Show direct subtypes only. --depth= Maximum transitive levels shown (default unlimited). --in= Only subtypes whose artifact label matches this glob (jar file name, JDK module or class-dir name). --exclude= Skip subtypes whose artifact label matches this glob. --limit= Maximum subtypes shown (default 50); the rest become a truncation footer. --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Type reference to list implementors of (full or short form). ``` ## Learn more Guide: [Usages, hierarchy & calls](https://mohammadmd1383.github.io/jdx/docs/navigating-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx callers IDE equivalent: Call hierarchy (in) Show who calls a method, transitively to --depth (default 1): one indented row per calling method, cycle-safe with …(cycle) markers. --in/--exclude filter rows by artifact label (jar file, JDK module or class-dir name). Member refs only (methods + constructors); type refs and field refs exit 3 (fields belong to jdx usages). Exits 1 when the method is unknown or uncalled, 2 on an ambiguous short name. ## Usage ```text jdx callers [] ``` ## Example ```console $ jdx callers 'com.google.gson.Gson#getAdapter(java.lang.Class)' --coord com.google.code.gson:gson:2.14.0 callers of 'com.google.gson.Gson#getAdapter(java.lang.Class)' call com.google.gson.internal.bind.JavaTimeTypeAdapters#localDateTime(com.google.gson.Gson) gson-2.14.0.jar call com.google.gson.internal.bind.JavaTimeTypeAdapters#offsetDateTime(com.google.gson.Gson) gson-2.14.0.jar call com.google.gson.internal.bind.JavaTimeTypeAdapters#offsetTime(com.google.gson.Gson) gson-2.14.0.jar call com.google.gson.internal.bind.JavaTimeTypeAdapters#zonedDateTime(com.google.gson.Gson) gson-2.14.0.jar call com.google.gson.internal.bind.ObjectTypeAdapter#write(com.google.gson.stream.JsonWriter, java.lang.Object) gson-2.14.0.jar call com.google.gson.internal.sql.SqlTimestampTypeAdapter$1#create(com.google.gson.Gson, com.google.gson.reflect.TypeToken) gson-2.14.0.jar warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` ## Options | Option | Description | |---|---| | `--depth=` | Maximum transitive levels shown (default 1, direct callers only). | | `--in=` | Only callers whose artifact label matches this glob (jar file name, JDK module or class-dir name). | | `--exclude=` | Skip callers whose artifact label matches this glob. | | `--limit=` | Maximum tree nodes shown (default 50); the rest become a truncation footer. | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Method or constructor reference to list callers of (full or short form). | ## Full help text ```console $ jdx callers --help Usage: jdx callers [] Show who calls a method, transitively to --depth (default 1): one indented row per calling method, cycle-safe with …(cycle) markers. --in/--exclude filter rows by artifact label (jar file, JDK module or class-dir name). Member refs only (methods + constructors); type refs and field refs exit 3 (fields belong to jdx usages). Exits 1 when the method is unknown or uncalled, 2 on an ambiguous short name. Options: --depth= Maximum transitive levels shown (default 1, direct callers only). --in= Only callers whose artifact label matches this glob (jar file name, JDK module or class-dir name). --exclude= Skip callers whose artifact label matches this glob. --limit= Maximum tree nodes shown (default 50); the rest become a truncation footer. --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Method or constructor reference to list callers of (full or short form). ``` ## Learn more Guide: [Usages, hierarchy & calls](https://mohammadmd1383.github.io/jdx/docs/navigating-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx calls IDE equivalent: Call hierarchy (out) Show what a method calls, transitively to --depth (default 1): one indented row per callee, cycle-safe with …(cycle) markers. --in/--exclude filter rows by artifact label; --external-only prunes callees in the method's own artifact. Member refs only (methods + constructors); type refs and field refs exit 3. Exits 1 when the method is unknown or calls nothing, 2 on an ambiguous short name. ## Usage ```text jdx calls [] ``` ## Example ```console $ jdx calls 'com.google.gson.Gson#toJson(java.lang.Object)' --coord com.google.code.gson:gson:2.14.0 calls from 'com.google.gson.Gson#toJson(java.lang.Object)' call com.google.gson.Gson#toJson(com.google.gson.JsonElement) gson-2.14.0.jar call com.google.gson.Gson#toJson(java.lang.Object, java.lang.reflect.Type) gson-2.14.0.jar call java.lang.Object#getClass() java.base warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` ## Options | Option | Description | |---|---| | `--depth=` | Maximum transitive levels shown (default 1, direct callees only). | | `--in=` | Only callees whose artifact label matches this glob (jar file name, JDK module or class-dir name). | | `--exclude=` | Skip callees whose artifact label matches this glob. | | `--external-only` | Hide callees in the queried method's own artifact (dependency view). | | `--limit=` | Maximum tree nodes shown (default 50); the rest become a truncation footer. | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Method or constructor reference to list callees of (full or short form). | ## Full help text ```console $ jdx calls --help Usage: jdx calls [] Show what a method calls, transitively to --depth (default 1): one indented row per callee, cycle-safe with …(cycle) markers. --in/--exclude filter rows by artifact label; --external-only prunes callees in the method's own artifact. Member refs only (methods + constructors); type refs and field refs exit 3. Exits 1 when the method is unknown or calls nothing, 2 on an ambiguous short name. Options: --depth= Maximum transitive levels shown (default 1, direct callees only). --in= Only callees whose artifact label matches this glob (jar file name, JDK module or class-dir name). --exclude= Skip callees whose artifact label matches this glob. --external-only Hide callees in the queried method's own artifact (dependency view). --limit= Maximum tree nodes shown (default 50); the rest become a truncation footer. --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Method or constructor reference to list callees of (full or short form). ``` ## Learn more Guide: [Usages, hierarchy & calls](https://mohammadmd1383.github.io/jdx/docs/navigating-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx samples IDE equivalent: Real call sites as usage examples Show real call sites as usage examples, ranked by exemplariness (non-test before test, non-generated before generated, fuller overloads first): one row per calling method with its enclosing-method source when paired sources exist, snippet-less otherwise. --limit caps the ranked rows (default 3); --prefer-sources ranks callers from sources-paired artifacts first; --in/--exclude filter rows by artifact label (jar file, JDK module or class-dir name). Type refs match calls to any member; field refs exit 3 (examples need a call site: jdx usages shows reads and writes). Exits 1 when the symbol is unknown or unsampled, 2 on an ambiguous short name. ## Usage ```text jdx samples [] ``` ## Example ```console $ jdx samples 'com.google.gson.JsonParser#parseString(String)' --coord com.google.code.gson:gson:2.14.0 samples of 'com.google.gson.JsonParser#parseString(String)' example com.google.gson.JsonParser#parse(java.lang.String) gson-2.14.0.jar com/google/gson/JsonParser.java:155-159 @Deprecated @InlineMe(replacement = "JsonParser.parseString(json)", imports = "com.google.gson.JsonParser") public JsonElement parse(String json) throws JsonSyntaxException { return parseString(json); } warning MULTI_RELEASE_VARIANT: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) ``` ## Options | Option | Description | |---|---| | `--limit=` | Maximum examples shown (default 3); the rest become a truncation footer. | | `--in=` | Only examples whose artifact label matches this glob (jar file name, JDK module or class-dir name). | | `--exclude=` | Skip examples whose artifact label matches this glob. | | `--prefer-sources` | Rank examples from sources-paired artifacts (rendered with snippets) ahead of snippet-less ones. | | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `--no-daemon` | Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Type or member reference to show usage examples of (full or short form). | ## Full help text ```console $ jdx samples --help Usage: jdx samples [] Show real call sites as usage examples, ranked by exemplariness (non-test before test, non-generated before generated, fuller overloads first): one row per calling method with its enclosing-method source when paired sources exist, snippet-less otherwise. --limit caps the ranked rows (default 3); --prefer-sources ranks callers from sources-paired artifacts first; --in/--exclude filter rows by artifact label (jar file, JDK module or class-dir name). Type refs match calls to any member; field refs exit 3 (examples need a call site: jdx usages shows reads and writes). Exits 1 when the symbol is unknown or unsampled, 2 on an ambiguous short name. Options: --limit= Maximum examples shown (default 3); the rest become a truncation footer. --in= Only examples whose artifact label matches this glob (jar file name, JDK module or class-dir name). --exclude= Skip examples whose artifact label matches this glob. --prefer-sources Rank examples from sources-paired artifacts (rendered with snippets) ahead of snippet-less ones. --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). --no-daemon Force in-process execution: do not forward this query to the background daemon even when its socket answers (PROPOSAL.md §14.1). -h, --help Show this message and exit Arguments: Type or member reference to show usage examples of (full or short form). ``` ## Learn more Guide: [Usages, hierarchy & calls](https://mohammadmd1383.github.io/jdx/docs/navigating-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx diff IDE equivalent: Public-API diff + binary-compat warnings (upgrades) Compare the public API of two artifacts and report every difference, most breaking first: removed members, narrowed visibility, changed descriptors, final/abstract/static flips, throws clauses and Kotlin-visible changes. Each finding names one rule (MEMBER_REMOVED, RETURN_TYPE_CHANGED, ...) so a caller can filter on the rule rather than on prose. Exits 0 whenever the comparison ran, whatever it found — add --fail-on breaking (or --fail-on any) to exit 1 in a pipeline. Exits 3 on a bad spec or flag, 5 when an artifact cannot be read. Each spec is a jar file, class directory, glob, or Maven coordinate (with --fetch). No workspace is needed. ## Usage ```text jdx diff [] ``` ## Example ```console $ jdx diff com.google.code.gson:gson:2.11.0 com.google.code.gson:gson:2.14.0 --fetch api diff com.google.code.gson:gson:2.11.0 -> com.google.code.gson:gson:2.14.0 (public surface) info TYPE_ADDED com.google.gson.internal.GsonTypes info TYPE_ADDED com.google.gson.internal.bind.JsonElementTypeAdapter info MEMBER_ADDED com.google.gson.FieldNamingStrategy#alternateNames(java.lang.reflect.Field) info MEMBER_ADDED com.google.gson.internal.ConstructorConstructor#get(com.google.gson.reflect.TypeToken, boolean) info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#BIG_DECIMAL_FACTORY info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#BIG_INTEGER_FACTORY info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#DOUBLE_STRICT info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#FLOAT_STRICT info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#LAZILY_PARSED_NUMBER_FACTORY info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#LONG_AS_STRING info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#atomicLongAdapter(com.google.gson.TypeAdapter) info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#atomicLongArrayAdapter(com.google.gson.TypeAdapter) info MEMBER_ADDED com.google.gson.internal.bind.TypeAdapters#javaTimeTypeAdapterFactory() info MEMBER_ADDED com.google.gson.internal.sql.SqlTypesSupport#SQL_TYPE_FACTORIES info MEMBER_ADDED com.google.gson.stream.JsonReader#getNestingLimit() info MEMBER_ADDED com.google.gson.stream.JsonReader#setNestingLimit(int) info FIELD_CONSTANT_VALUE_CHANGED com.google.gson.internal.GsonBuildConfig#VERSION: "2.11.0" -> "2.14.0" 0 breaking · 0 suspicious · 17 informational · 2 types added · 0 types removed warning MULTI_RELEASE_VARIANT: in com.google.code.gson:gson:2.11.0: multi-release gson-2.11.0.jar serves version-specific variants for this JDK (21) warning MULTI_RELEASE_VARIANT: in com.google.code.gson:gson:2.14.0: multi-release gson-2.14.0.jar serves version-specific variants for this JDK (21) warning UNNAMEABLE_CLASS: in com.google.code.gson:gson:2.11.0: unnameable class name (empty name segment in [, Gson, Preconditions]): com/google/gson/internal/$Gson$Preconditions.class warning UNNAMEABLE_CLASS: in com.google.code.gson:gson:2.11.0: unnameable class name (empty name segment in [, Gson, Types]): com/google/gson/internal/$Gson$Types.class warning UNNAMEABLE_CLASS: in com.google.code.gson:gson:2.11.0: unnameable class name (empty name segment in [, Gson, Types, GenericArrayTypeImpl]): com/google/gson/internal/$Gson$Types$GenericArrayTypeImpl.class warning UNNAMEABLE_CLASS: in com.google.code.gson:gson:2.11.0: unnameable class name (empty name segment in [, Gson, Types, ParameterizedTypeImpl]): com/google/gson/internal/$Gson$Types$ParameterizedTypeImpl.class warning UNNAMEABLE_CLASS: in com.google.code.gson:gson:2.11.0: unnameable class name (empty name segment in [, Gson, Types, WildcardTypeImpl]): com/google/gson/internal/$Gson$Types$WildcardTypeImpl.class source: com.google.code.gson:gson:2.11.0 (bytecode) source: com.google.code.gson:gson:2.14.0 (bytecode) next: jdx members com.google.gson.internal.GsonTypes ``` ## Options | Option | Description | |---|---| | `--visibility=(public\|all)` | Which declarations to compare: public (the default — public and protected types and members, the surface a consumer compiles against) or all (every declared type and member, for refactors and mod/mixin work). | | `--include-synthetic` | Compare bridge/synthetic members too, hidden by default. | | `--severity=(all\|suspicious\|breaking)` | How much of the report to print: all (the default), suspicious (breaking and suspicious), or breaking only. The per-severity tally always describes the whole comparison, never only what was printed. | | `--fail-on=(none\|breaking\|any)` | Turn the report into a process status: breaking exits 1 when a breaking change is present, any exits 1 on any difference. Without it a successful comparison always exits 0. | | `--limit=` | Maximum findings shown (default 200); the rest become a truncation footer. The tail this cuts is by construction the least severe. | | `--fetch` | Allow downloading a Maven coordinate from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from ~/.gradle/caches and ~/.m2 only. | | `--repo=` | Maven repository base URL for --fetch (repeatable). Tried in flag order before Maven Central, which stays last as the fallback. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `--no-color` | Disable ANSI colors even on a TTY (piped output is always plain). | | `-h, --help` | Show this message and exit | ## Arguments | Argument | Description | |---|---| | `` | Baseline artifact: jar, class dir, glob, or group:artifact:version. | | `` | Candidate artifact, compared against the baseline. | ## Full help text ```console $ jdx diff --help Usage: jdx diff [] Compare the public API of two artifacts and report every difference, most breaking first: removed members, narrowed visibility, changed descriptors, final/abstract/static flips, throws clauses and Kotlin-visible changes. Each finding names one rule (MEMBER_REMOVED, RETURN_TYPE_CHANGED, ...) so a caller can filter on the rule rather than on prose. Exits 0 whenever the comparison ran, whatever it found — add --fail-on breaking (or --fail-on any) to exit 1 in a pipeline. Exits 3 on a bad spec or flag, 5 when an artifact cannot be read. Each spec is a jar file, class directory, glob, or Maven coordinate (with --fetch). No workspace is needed. Options: --visibility=(public|all) Which declarations to compare: public (the default — public and protected types and members, the surface a consumer compiles against) or all (every declared type and member, for refactors and mod/mixin work). --include-synthetic Compare bridge/synthetic members too, hidden by default. --severity=(all|suspicious|breaking) How much of the report to print: all (the default), suspicious (breaking and suspicious), or breaking only. The per-severity tally always describes the whole comparison, never only what was printed. --fail-on=(none|breaking|any) Turn the report into a process status: breaking exits 1 when a breaking change is present, any exits 1 on any difference. Without it a successful comparison always exits 0. --limit= Maximum findings shown (default 200); the rest become a truncation footer. The tail this cuts is by construction the least severe. --fetch Allow downloading a Maven coordinate from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from ~/.gradle/caches and ~/.m2 only. --repo= Maven repository base URL for --fetch (repeatable). Tried in flag order before Maven Central, which stays last as the fallback. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). --no-color Disable ANSI colors even on a TTY (piped output is always plain). -h, --help Show this message and exit Arguments: Baseline artifact: jar, class dir, glob, or group:artifact:version. Candidate artifact, compared against the baseline. ``` ## Learn more Guide: [API diff for upgrades](https://mohammadmd1383.github.io/jdx/docs/api-diff/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx batch IDE equivalent: Many queries, one process (NDJSON in/out) Answer many queries in one process: read NDJSON RpcRequest lines ({"command":"members","query":"…","params":{…}}) from stdin, print one --json envelope per line on stdout. Per-query failures ride their own envelope and never abort the stream; the exit code is the maximum query exit code. Output is always envelopes (--json is accepted and ignored). Roots resolve once from --jars/--src/-w/--no-jdk/--coord/--repo/--fetch. Example: echo '{"command":"show","query":"java.util.Map"}' | jdx batch. ## Usage ```text jdx batch [] ``` ## Example ```console $ echo '{"command":"show","query":"java.util.Map"}' | jdx batch {"jdx":1,"ok":true,"command":"show","query":"java.util.Map","result":{"kind":"interface","declaration":"public abstract interface Map","modifiers":["public","abstract"],"interfaces":[],"counts":{"constructors":0,"methods":39,"fields":0}},"warnings":[],"provenance":[{"artifact":"java.base","origin":"jrt"}]} ``` ## Options | Option | Description | |---|---| | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `--src=` | Source-dir roots: directories of .java/.kt files scanned textually for whole-word mentions (repeatable). Merge in front of the selected workspace's stored srcs. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. | | `--repo=` | Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--fetch` | Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. | | `--json` | Accepted and ignored: batch output is always the --json envelope, one per line. | | `-h, --help` | Show this message and exit | ## Full help text ```console $ jdx batch --help Usage: jdx batch [] Answer many queries in one process: read NDJSON RpcRequest lines ({"command":"members","query":"…","params":{…}}) from stdin, print one --json envelope per line on stdout. Per-query failures ride their own envelope and never abort the stream; the exit code is the maximum query exit code. Output is always envelopes (--json is accepted and ignored). Roots resolve once from --jars/--src/-w/--no-jdk/--coord/--repo/--fetch. Example: echo '{"command":"show","query":"java.util.Map"}' | jdx batch. Options: --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. --src= Source-dir roots: directories of .java/.kt files scanned textually for whole-word mentions (repeatable). Merge in front of the selected workspace's stored srcs. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). Resolved from ~/.gradle/caches and ~/.m2 first; with --fetch, downloaded from Maven repositories into /m2 with checksum verification. Merges in front of the workspace. --repo= Maven repository base URL for --coord fetches (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. --fetch Allow downloading --coord artifacts (and their -sources.jar) from Maven repositories (Central by default, --repo to add mirrors). Without it, coordinates resolve from the local caches only. --json Accepted and ignored: batch output is always the --json envelope, one per line. -h, --help Show this message and exit ``` ## Learn more Guide: [MCP, daemon, HTTP & batch](https://mohammadmd1383.github.io/jdx/docs/serving/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx bench IDE equivalent: Benchmark the read path (timings + §15 targets) Benchmark the read path: run the fixed load/show/members/search/hierarchy workload against the resolved roots and print the timing table with PROPOSAL.md §15 targets. Roots resolve like the read commands (--jars plus the selected workspace plus the JDK unless --no-jdk); with no explicit roots and no workspace selected, the local minecraft-client.jar is used when present. Targets are advisory: success always exits 0. Example: jdx bench --iterations 1. ## Usage ```text jdx bench [] ``` ## Options | Option | Description | |---|---| | `--jars=` | Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. | | `--src=` | Source-dir roots (repeatable). Merge in front of the selected workspace's stored srcs. | | `-w, --workspace=` | Use a named workspace (see jdx ws). Explicit --jars merge in front of it. | | `--no-jdk` | Do not include the running JDK's stdlib (included by default). | | `--coord=` | Maven coordinate root group:artifact:version (repeatable). | | `--repo=` | Maven repository base URL for --coord fetches (repeatable). | | `--fetch` | Allow downloading --coord artifacts from Maven repositories. | | `--iterations=` | Runs per case; the median is reported (default 3). | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | ## Full help text ```console $ jdx bench --help Usage: jdx bench [] Benchmark the read path: run the fixed load/show/members/search/hierarchy workload against the resolved roots and print the timing table with PROPOSAL.md §15 targets. Roots resolve like the read commands (--jars plus the selected workspace plus the JDK unless --no-jdk); with no explicit roots and no workspace selected, the local minecraft-client.jar is used when present. Targets are advisory: success always exits 0. Example: jdx bench --iterations 1. Options: --jars= Binary roots: jar files, class directories or globs (repeatable). Merge in front of the selected workspace's roots. --src= Source-dir roots (repeatable). Merge in front of the selected workspace's stored srcs. -w, --workspace= Use a named workspace (see jdx ws). Explicit --jars merge in front of it. --no-jdk Do not include the running JDK's stdlib (included by default). --coord= Maven coordinate root group:artifact:version (repeatable). --repo= Maven repository base URL for --coord fetches (repeatable). --fetch Allow downloading --coord artifacts from Maven repositories. --iterations= Runs per case; the median is reported (default 3). --json Emit machine-readable JSON instead of human-readable text (same information, D-007). -h, --help Show this message and exit ``` ## Learn more Guide: [MCP, daemon, HTTP & batch](https://mohammadmd1383.github.io/jdx/docs/serving/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx serve IDE equivalent: Local HTTP/JSON API Serve the HTTP/JSON API: GET /v1/?query=…&=… for each jdx query, POST /v1/batch for NDJSON batches, GET /v1/health as a liveness probe. Answers are the exact --json envelopes over one stored workspace (default: default), bound to localhost by default. Blocks until interrupted. ## Usage ```text jdx serve [] ``` ## Options | Option | Description | |---|---| | `-w, --workspace=` | Stored workspace the server answers from (default: default). Individual requests may override it per call with ?workspace=. | | `--port=` | Port to listen on (default 7070). | | `--bind=` | Address to bind (default 127.0.0.1; localhost only unless changed). | | `-h, --help` | Show this message and exit | ## Full help text ```console $ jdx serve --help Usage: jdx serve [] Serve the HTTP/JSON API: GET /v1/?query=…&=… for each jdx query, POST /v1/batch for NDJSON batches, GET /v1/health as a liveness probe. Answers are the exact --json envelopes over one stored workspace (default: default), bound to localhost by default. Blocks until interrupted. Options: -w, --workspace= Stored workspace the server answers from (default: default). Individual requests may override it per call with ?workspace=. --port= Port to listen on (default 7070). --bind= Address to bind (default 127.0.0.1; localhost only unless changed). -h, --help Show this message and exit ``` ## Learn more Guide: [MCP, daemon, HTTP & batch](https://mohammadmd1383.github.io/jdx/docs/serving/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx setup IDE equivalent: Wire jdx into an agent (OpenCode, Claude Code, Cursor, Kilo Code, Cline, Codex CLI, GitHub Copilot CLI, Antigravity) Wire jdx into an AI agent: write the MCP server entries that launch `jdx mcp` into the agent config (project checkout or user-global). --agent opencode|claude-code|cursor|kilo|cline|codex|copilot|antigravity --scope project|system. OpenCode writes both the v1 (mcp.jdx) and v2 (mcp.servers.jdx) entries so either OpenCode line picks it up (v1 support is deprecated, slated for removal); Claude Code writes the mcpServers.jdx entry into .mcp.json (project) or ~/.claude.json (system); Cursor writes the mcpServers.jdx entry into .cursor/mcp.json (project) or ~/.cursor/mcp.json (system); Kilo Code gets the single mcp.jdx entry in kilo.jsonc (project prefers .kilo/); Cline writes the mcpServers.jdx transport entry into the single global cline_mcp_settings.json for either scope (Cline keeps no project-level MCP file). Codex CLI gets the [mcp_servers.jdx] table in .codex/config.toml (project) or ~/.codex/config.toml (system, $CODEX_HOME honoured); GitHub Copilot CLI gets the mcpServers.jdx entry in .mcp.json (project) or ~/.copilot/mcp-config.json (system, $COPILOT_HOME honoured); Antigravity gets the mcpServers.jdx entry in .agents/mcp_config.json (project) or ~/.gemini/config/mcp_config.json (system). --check reports without writing; --remove uninstalls cleanly. Merges (never clobbers unrelated entries); re-runs are no-ops. Exits 0 installed/removed/present, 1 checked-absent, 3 bad usage, 5 unreadable config. ## Usage ```text jdx setup [] ``` ## Options | Option | Description | |---|---| | `--agent=` | Agent to wire: opencode, claude-code (claude is accepted as an alias), cursor, kilo (Kilo Code, an OpenCode fork), cline (Cline), codex (Codex CLI), copilot (GitHub Copilot CLI), or antigravity (agy is accepted as an alias). | | `--scope=` | Where to write: project (default) or system. OpenCode targets opencode.json[c] (~/.config/opencode); Claude Code targets .mcp.json (project) or ~/.claude.json (system); Cursor targets .cursor/mcp.json (project) or ~/.cursor/mcp.json (system); Kilo Code targets kilo.json[c] (~/.config/kilo, project prefers .kilo/); Cline targets ~/.cline/data/settings/cline_mcp_settings.json for both scopes (single global file). Codex CLI targets .codex/config.toml (project) or ~/.codex/config.toml (system, $CODEX_HOME honoured); GitHub Copilot CLI targets .mcp.json (project) or ~/.copilot/mcp-config.json (system, $COPILOT_HOME honoured); Antigravity targets .agents/mcp_config.json (project) or ~/.gemini/config/mcp_config.json (system). | | `--check` | Report whether the entry is present without writing anything. | | `--remove` | Remove the jdx MCP entry, leaving every other entry untouched. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | ## Full help text ```console $ jdx setup --help Usage: jdx setup [] Wire jdx into an AI agent: write the MCP server entries that launch `jdx mcp` into the agent config (project checkout or user-global). --agent opencode|claude-code|cursor|kilo|cline|codex|copilot|antigravity --scope project|system. OpenCode writes both the v1 (mcp.jdx) and v2 (mcp.servers.jdx) entries so either OpenCode line picks it up (v1 support is deprecated, slated for removal); Claude Code writes the mcpServers.jdx entry into .mcp.json (project) or ~/.claude.json (system); Cursor writes the mcpServers.jdx entry into .cursor/mcp.json (project) or ~/.cursor/mcp.json (system); Kilo Code gets the single mcp.jdx entry in kilo.jsonc (project prefers .kilo/); Cline writes the mcpServers.jdx transport entry into the single global cline_mcp_settings.json for either scope (Cline keeps no project-level MCP file). Codex CLI gets the [mcp_servers.jdx] table in .codex/config.toml (project) or ~/.codex/config.toml (system, $CODEX_HOME honoured); GitHub Copilot CLI gets the mcpServers.jdx entry in .mcp.json (project) or ~/.copilot/mcp-config.json (system, $COPILOT_HOME honoured); Antigravity gets the mcpServers.jdx entry in .agents/mcp_config.json (project) or ~/.gemini/config/mcp_config.json (system). --check reports without writing; --remove uninstalls cleanly. Merges (never clobbers unrelated entries); re-runs are no-ops. Exits 0 installed/removed/present, 1 checked-absent, 3 bad usage, 5 unreadable config. Options: --agent= Agent to wire: opencode, claude-code (claude is accepted as an alias), cursor, kilo (Kilo Code, an OpenCode fork), cline (Cline), codex (Codex CLI), copilot (GitHub Copilot CLI), or antigravity (agy is accepted as an alias). --scope= Where to write: project (default) or system. OpenCode targets opencode.json[c] (~/.config/opencode); Claude Code targets .mcp.json (project) or ~/.claude.json (system); Cursor targets .cursor/mcp.json (project) or ~/.cursor/mcp.json (system); Kilo Code targets kilo.json[c] (~/.config/kilo, project prefers .kilo/); Cline targets ~/.cline/data/settings/cline_mcp_settings.json for both scopes (single global file). Codex CLI targets .codex/config.toml (project) or ~/.codex/config.toml (system, $CODEX_HOME honoured); GitHub Copilot CLI targets .mcp.json (project) or ~/.copilot/mcp-config.json (system, $COPILOT_HOME honoured); Antigravity targets .agents/mcp_config.json (project) or ~/.gemini/config/mcp_config.json (system). --check Report whether the entry is present without writing anything. --remove Remove the jdx MCP entry, leaving every other entry untouched. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). -h, --help Show this message and exit ``` ## Learn more Guide: [Use with AI agents](https://mohammadmd1383.github.io/jdx/docs/ai-agents/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx mcp IDE equivalent: MCP stdio server (typed jdx_* tools) Serve the MCP stdio server: each jdx query as a typed jdx_* tool with a generated schema, for agents that speak Model Context Protocol. Holds the workspace in memory for the session, so repeated queries are instant. MCP clients spawn this as a child process and talk JSON-RPC over stdin/stdout. ## Usage ```text jdx mcp [] ``` ## Options | Option | Description | |---|---| | `-w, --workspace=` | Stored workspace the server answers from (default: default). A missing workspace reads as an error naming the fix; individual tool calls may override it per call. | | `-h, --help` | Show this message and exit | ## Full help text ```console $ jdx mcp --help Usage: jdx mcp [] Serve the MCP stdio server: each jdx query as a typed jdx_* tool with a generated schema, for agents that speak Model Context Protocol. Holds the workspace in memory for the session, so repeated queries are instant. MCP clients spawn this as a child process and talk JSON-RPC over stdin/stdout. Options: -w, --workspace= Stored workspace the server answers from (default: default). A missing workspace reads as an error naming the fix; individual tool calls may override it per call. -h, --help Show this message and exit ``` ## Learn more Guide: [MCP, daemon, HTTP & batch](https://mohammadmd1383.github.io/jdx/docs/serving/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx help IDE equivalent: This cheat sheet Print the jdx cheat sheet. With --agent, print the compact paste-ready block for CLAUDE.md / system prompts. With --json, print the same rows in the JSON envelope. ## Usage ```text jdx help [] ``` ## Options | Option | Description | |---|---| | `--agent` | Print the compact paste-ready cheat sheet for agents instead of the human layout. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | ## Full help text ```console $ jdx help --help Usage: jdx help [] Print the jdx cheat sheet. With --agent, print the compact paste-ready block for CLAUDE.md / system prompts. With --json, print the same rows in the JSON envelope. Options: --agent Print the compact paste-ready cheat sheet for agents instead of the human layout. --json Emit machine-readable JSON instead of human-readable text (same information, D-007). -h, --help Show this message and exit ``` ## Learn more Guide: [Use with AI agents](https://mohammadmd1383.github.io/jdx/docs/ai-agents/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx daemon IDE equivalent: Warm background JVM (5-min idle shutdown) Manage the background daemon: a hot JVM answering queries over a unix socket (start, stop, status, restart). The daemon shuts itself down after --idle without requests (default 5m, 0 disables). ## Usage ```text jdx daemon [] []... ``` ## Subcommands ### `jdx daemon start` Start the daemon for a workspace (idempotent: already running exits 0). Spawns a background JVM and waits until its socket answers health. ```text jdx daemon start [] ``` | Option | Description | |---|---| | `--workspace=` | Workspace the daemon serves. | | `--idle=` | Idle shutdown after this long without requests (e.g. 30s, 5m, 2h; 0 disables). | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | ### `jdx daemon stop` Stop the daemon for a workspace. Exits 1 when no daemon is running. ```text jdx daemon stop [] ``` | Option | Description | |---|---| | `--workspace=` | Workspace the daemon serves. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | ### `jdx daemon status` Report the daemon for a workspace: uptime, memory, indexed artifacts, query count. Exits 1 when no daemon is running. ```text jdx daemon status [] ``` | Option | Description | |---|---| | `--workspace=` | Workspace the daemon serves. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | ### `jdx daemon restart` Restart the daemon for a workspace: stop when running, then start. ```text jdx daemon restart [] ``` | Option | Description | |---|---| | `--workspace=` | Workspace the daemon serves. | | `--idle=` | Idle shutdown after this long without requests (e.g. 30s, 5m, 2h; 0 disables). | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | ### `jdx daemon run` Run the daemon in the foreground (internal: daemon start spawns this). ```text jdx daemon run [] ``` | Option | Description | |---|---| | `--workspace=` | Workspace the daemon serves. | | `--idle=` | Idle shutdown after this long without requests (e.g. 30s, 5m, 2h; 0 disables). | | `-h, --help` | Show this message and exit | ## Options | Option | Description | |---|---| | `-h, --help` | Show this message and exit | ## Full help text ```console $ jdx daemon --help Usage: jdx daemon [] []... Manage the background daemon: a hot JVM answering queries over a unix socket (start, stop, status, restart). The daemon shuts itself down after --idle without requests (default 5m, 0 disables). Options: -h, --help Show this message and exit Commands: start Start the daemon for a workspace (idempotent: already running exits 0). Spawns a background JVM and waits until its socket answers health. stop Stop the daemon for a workspace. Exits 1 when no daemon is running. status Report the daemon for a workspace: uptime, memory, indexed artifacts, query count. Exits 1 when no daemon is running. restart Restart the daemon for a workspace: stop when running, then start. run Run the daemon in the foreground (internal: daemon start spawns this). ``` ## Learn more Guide: [MCP, daemon, HTTP & batch](https://mohammadmd1383.github.io/jdx/docs/serving/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx ws IDE equivalent: Project and library configuration Manage named workspaces: ordered jar roots reused across queries (create, list, info, remove, use, add). Read commands consume them via -w , JDX_WORKSPACE, or jdx ws use. ## Usage ```text jdx ws [] []... ``` ## Subcommands ### `jdx ws create` Create a named workspace: an ordered list of binary roots reused via -w . Refuses when the name exists (see ws add, ws remove). Exits 1 when the name exists, 3 on bad usage, 6 on IO failure. ```text jdx ws create [] ``` | Option | Description | |---|---| | `--jars=` | Binary roots: jar files, class directories or globs (repeatable, order kept). | | `--jdk` | Include the JDK stdlib (the default). | | `--no-jdk` | Do not include the JDK stdlib. | | `--src=` | Source dirs: directories of .java/.kt files scanned textually by jdx usages (repeatable, order kept). | | `--coord=` | Maven coordinate roots group:artifact:version (repeatable). Stored in the workspace and resolved to jars at query time (local caches first; --fetch on read commands allows Maven repository downloads). | | `--repo=` | Maven repository base URLs for --coord fetches (repeatable). Stored in the workspace and tried in order before Maven Central at query time. Must be http(s) URLs. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | | Argument | Description | |---|---| | `` | Workspace name (letters, digits, '.', '_' and '-'). | ### `jdx ws list` List named workspaces (the default from jdx ws use is marked). Exits 6 on IO failure. ```text jdx ws list [] ``` | Option | Description | |---|---| | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | ### `jdx ws info` Show a workspace: ordered roots, JDK switch, whether it is the default. Exits 1 when unknown, 4 when its file is corrupt. ```text jdx ws info [] ``` | Option | Description | |---|---| | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | | Argument | Description | |---|---| | `` | Workspace name to show. | ### `jdx ws remove` Delete a named workspace (clears the jdx ws use default when it pointed here). Exits 1 when unknown, 6 on IO failure. ```text jdx ws remove [] ``` | Option | Description | |---|---| | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | | Argument | Description | |---|---| | `` | Workspace name to delete. | ### `jdx ws use` Set the default workspace read commands use when neither -w nor JDX_WORKSPACE names one (PROPOSAL.md §13). With --clear, unset it. Exits 1 when the workspace is unknown, 3 on bad usage. ```text jdx ws use [] [] ``` | Option | Description | |---|---| | `--clear` | Unset the default workspace. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | | Argument | Description | |---|---| | `` | Workspace name to make the default. | ### `jdx ws add` Append one binary root (jar, class dir or glob) to a workspace, keeping order. Exits 1 when the workspace is unknown, 3 on bad usage, 4/6 on store failure. ```text jdx ws add [] ``` | Option | Description | |---|---| | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | | Argument | Description | |---|---| | `` | Workspace to extend. | | `` | Binary root to append (jar, class dir or glob). | ## Options | Option | Description | |---|---| | `-h, --help` | Show this message and exit | ## Full help text ```console $ jdx ws --help Usage: jdx ws [] []... Manage named workspaces: ordered jar roots reused across queries (create, list, info, remove, use, add). Read commands consume them via -w , JDX_WORKSPACE, or jdx ws use. Options: -h, --help Show this message and exit Commands: create Create a named workspace: an ordered list of binary roots reused via -w . Refuses when the name exists (see ws add, ws remove). Exits 1 when the name exists, 3 on bad usage, 6 on IO failure. list List named workspaces (the default from jdx ws use is marked). Exits 6 on IO failure. info Show a workspace: ordered roots, JDK switch, whether it is the default. Exits 1 when unknown, 4 when its file is corrupt. remove Delete a named workspace (clears the jdx ws use default when it pointed here). Exits 1 when unknown, 6 on IO failure. use Set the default workspace read commands use when neither -w nor JDX_WORKSPACE names one (PROPOSAL.md §13). With --clear, unset it. Exits 1 when the workspace is unknown, 3 on bad usage. add Append one binary root (jar, class dir or glob) to a workspace, keeping order. Exits 1 when the workspace is unknown, 3 on bad usage, 4/6 on store failure. ``` ## Learn more Guide: [Classpath & workspaces](https://mohammadmd1383.github.io/jdx/docs/classpath/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx cache IDE equivalent: Index and cache maintenance Manage the index cache: report sizes (info), evict unreferenced artifacts (gc), wipe the regenerable cache (clear). Everything removed is re-indexed or re-derived on demand. ## Usage ```text jdx cache [] []... ``` ## Subcommands ### `jdx cache info` Report the index cache: database location, size and schema, artifact and class counts, total cache size. A missing database is an empty report, not an error. Exits 6 when the database cannot be read. ```text jdx cache info [] ``` | Option | Description | |---|---| | `--cache-dir=` | Cache root to report (default per-OS cache dir, PROPOSAL.md §17.1). | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | ### `jdx cache gc` Evict cached artifacts no workspace references, plus stale ones whose file is gone, plus orphan daemon logs (kept while their daemon answers). Artifacts still named by a workspace are kept; the JDK index is kept while any workspace includes the JDK. With --dry-run, report without deleting. Exits 4 on a corrupt workspace, 6 on DB/IO failure. ```text jdx cache gc [] ``` | Option | Description | |---|---| | `--cache-dir=` | Cache root to collect (default per-OS cache dir, PROPOSAL.md §17.1). | | `--dry-run` | Report what would be deleted without deleting anything. | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | ### `jdx cache clear` Wipe the regenerable cache: the index database files and the derived project cache. An absent cache is success with nothing deleted. Exits 6 when files cannot be deleted. ```text jdx cache clear [] ``` | Option | Description | |---|---| | `--cache-dir=` | Cache root to wipe (default per-OS cache dir, PROPOSAL.md §17.1). | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | ## Options | Option | Description | |---|---| | `-h, --help` | Show this message and exit | ## Full help text ```console $ jdx cache --help Usage: jdx cache [] []... Manage the index cache: report sizes (info), evict unreferenced artifacts (gc), wipe the regenerable cache (clear). Everything removed is re-indexed or re-derived on demand. Options: -h, --help Show this message and exit Commands: info Report the index cache: database location, size and schema, artifact and class counts, total cache size. A missing database is an empty report, not an error. Exits 6 when the database cannot be read. gc Evict cached artifacts no workspace references, plus stale ones whose file is gone, plus orphan daemon logs (kept while their daemon answers). Artifacts still named by a workspace are kept; the JDK index is kept while any workspace includes the JDK. With --dry-run, report without deleting. Exits 4 on a corrupt workspace, 6 on DB/IO failure. clear Wipe the regenerable cache: the index database files and the derived project cache. An absent cache is success with nothing deleted. Exits 6 when files cannot be deleted. ``` ## Learn more Guide: [Classpath & workspaces](https://mohammadmd1383.github.io/jdx/docs/classpath/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx kotlin IDE equivalent: Fetch the Kotlin PSI sidecar (Kotlin sources) Manage the Kotlin PSI sidecar: install the side-loaded compiler set so body/source/doc can serve Kotlin sources. Without it, Kotlin binaries still render via @Metadata and .kt queries degrade. ## Usage ```text jdx kotlin [] []... ``` ## Subcommands ### `jdx kotlin install` Download and verify the Kotlin PSI sidecar set (kotlin-compiler-embeddable plus its runtime jars, at pinned versions with SHA-1 verification) into /kotlin. Jars already present are skipped without network; re-run resumes after a failure. Exits 5 when a download, checksum or write fails. ```text jdx kotlin install [] ``` | Option | Description | |---|---| | `--repo=` | Maven repository base URL to fetch from (repeatable, e.g. --repo https://repo.example.com/maven2). Tried in flag order before Maven Central, which stays last as the fallback. Must be an http(s) URL. | | `--force` | Re-download every jar even when present (repairs a corrupt sidecar that probes as installed but fails to initialise). | | `--json` | Emit machine-readable JSON instead of human-readable text (same information, D-007). | | `-h, --help` | Show this message and exit | ## Options | Option | Description | |---|---| | `-h, --help` | Show this message and exit | ## Full help text ```console $ jdx kotlin --help Usage: jdx kotlin [] []... Manage the Kotlin PSI sidecar: install the side-loaded compiler set so body/source/doc can serve Kotlin sources. Without it, Kotlin binaries still render via @Metadata and .kt queries degrade. Options: -h, --help Show this message and exit Commands: install Download and verify the Kotlin PSI sidecar set (kotlin-compiler-embeddable plus its runtime jars, at pinned versions with SHA-1 verification) into /kotlin. Jars already present are skipped without network; re-run resumes after a failure. Exits 5 when a download, checksum or write fails. ``` ## Learn more Guide: [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/) · [All commands](https://mohammadmd1383.github.io/jdx/docs/reference/) --- # jdx for AI coding agents Connect jdx to OpenCode, Claude Code, Cursor, Kilo Code, Cline, Codex CLI, GitHub Copilot CLI, Antigravity or any MCP client: one command gives your coding agent Java and Kotlin code navigation. - [OpenCode](https://mohammadmd1383.github.io/jdx/integrations/opencode/): `jdx setup --agent opencode --scope project` - [Claude Code](https://mohammadmd1383.github.io/jdx/integrations/claude-code/): `jdx setup --agent claude-code --scope project` - [Cursor](https://mohammadmd1383.github.io/jdx/integrations/cursor/): `jdx setup --agent cursor --scope project` - [Kilo Code](https://mohammadmd1383.github.io/jdx/integrations/kilo/): `jdx setup --agent kilo --scope project` - [Cline](https://mohammadmd1383.github.io/jdx/integrations/cline/): `jdx setup --agent cline --scope project` - [Codex CLI](https://mohammadmd1383.github.io/jdx/integrations/codex/): `jdx setup --agent codex --scope project` - [GitHub Copilot CLI](https://mohammadmd1383.github.io/jdx/integrations/copilot/): `jdx setup --agent copilot --scope project` - [Antigravity](https://mohammadmd1383.github.io/jdx/integrations/antigravity/): `jdx setup --agent antigravity --scope project` ## Any MCP client ```json {"mcpServers":{"jdx":{"command":"jdx","args":["mcp"]}}} ``` Tools: https://mohammadmd1383.github.io/jdx/docs/reference/mcp-tools/ --- # jdx for OpenCode Give OpenCode an IDE for Java, Kotlin and JVM jars: wire the jdx MCP server in one command, then ask for members, bodies, usages and call graphs. ## Set up ```console $ curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash $ jdx setup --agent opencode --scope project ``` What it writes: OpenCode writes both the v1 (mcp.jdx) and v2 (mcp.servers.jdx) entries so either OpenCode line picks it up (v1 support is deprecated, slated for removal). ## Manage - `jdx setup --agent opencode --scope system` — Or wire it for every project (user-global config). - `jdx setup --agent opencode --scope project --check` — Report whether it is wired, without writing anything. - `jdx setup --agent opencode --scope project --remove` — Remove the entry again (other entries are never touched). ## Tools - `jdx_show` — Go to declaration: the class card for a type (kind, supertypes, source, provenance). - `jdx_members` — List the members of a type — the '.' completion equivalent. - `jdx_outline` — File Structure: one dense line per member declared on a type (never inherited). - `jdx_body` — Open the method: the source body of one member, sliced verbatim with provenance. - `jdx_source` — Open the file: whole source for a type, or a slice of it, with provenance. - `jdx_signature` — Parameter info: one signature line per overload, with real parameter names. - `jdx_doc` — Quick documentation for a type or member, falling back to the nearest documenting supertype like IntelliJ quick-doc. - `jdx_search` — Search everywhere: symbols by glob, regex or fuzzy name, with did-you-mean. - `jdx_resolve` — What is this symbol? - `jdx_ls` — External library browser: packages in the workspace roots. - `jdx_tree` — External library browser: artifacts and their package trees. - `jdx_usages` — Find usages of a type or member across the workspace jars and source dirs. - `jdx_hierarchy` — Type hierarchy: supertypes upward and subtypes downward across the workspace. - `jdx_implementors` — Who implements this interface or extends this class, transitively. - `jdx_callers` — Call hierarchy in: who calls this method, as a depth-bounded tree. - `jdx_calls` — Call hierarchy out: what this method calls, as a depth-bounded tree. - `jdx_samples` — How is this symbol really used? - `jdx_diff` — What changed between two artifacts? - `jdx_version` — The jdx version this server was built as. - `jdx_doctor` — Environment report. - `jdx_health` — Liveness probe: uptime, workspace served and queries answered. --- # jdx for Claude Code Give Claude Code an IDE for Java, Kotlin and JVM jars: wire the jdx MCP server in one command, then ask for members, bodies, usages and call graphs. ## Set up ```console $ curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash $ jdx setup --agent claude-code --scope project ``` What it writes: Claude Code writes the mcpServers.jdx entry into .mcp.json (project) or ~/.claude.json (system). ## Manage - `jdx setup --agent claude-code --scope system` — Or wire it for every project (user-global config). - `jdx setup --agent claude-code --scope project --check` — Report whether it is wired, without writing anything. - `jdx setup --agent claude-code --scope project --remove` — Remove the entry again (other entries are never touched). ## Tools - `jdx_show` — Go to declaration: the class card for a type (kind, supertypes, source, provenance). - `jdx_members` — List the members of a type — the '.' completion equivalent. - `jdx_outline` — File Structure: one dense line per member declared on a type (never inherited). - `jdx_body` — Open the method: the source body of one member, sliced verbatim with provenance. - `jdx_source` — Open the file: whole source for a type, or a slice of it, with provenance. - `jdx_signature` — Parameter info: one signature line per overload, with real parameter names. - `jdx_doc` — Quick documentation for a type or member, falling back to the nearest documenting supertype like IntelliJ quick-doc. - `jdx_search` — Search everywhere: symbols by glob, regex or fuzzy name, with did-you-mean. - `jdx_resolve` — What is this symbol? - `jdx_ls` — External library browser: packages in the workspace roots. - `jdx_tree` — External library browser: artifacts and their package trees. - `jdx_usages` — Find usages of a type or member across the workspace jars and source dirs. - `jdx_hierarchy` — Type hierarchy: supertypes upward and subtypes downward across the workspace. - `jdx_implementors` — Who implements this interface or extends this class, transitively. - `jdx_callers` — Call hierarchy in: who calls this method, as a depth-bounded tree. - `jdx_calls` — Call hierarchy out: what this method calls, as a depth-bounded tree. - `jdx_samples` — How is this symbol really used? - `jdx_diff` — What changed between two artifacts? - `jdx_version` — The jdx version this server was built as. - `jdx_doctor` — Environment report. - `jdx_health` — Liveness probe: uptime, workspace served and queries answered. --- # jdx for Cursor Give Cursor an IDE for Java, Kotlin and JVM jars: wire the jdx MCP server in one command, then ask for members, bodies, usages and call graphs. ## Set up ```console $ curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash $ jdx setup --agent cursor --scope project ``` What it writes: Cursor writes the mcpServers.jdx entry into .cursor/mcp.json (project) or ~/.cursor/mcp.json (system). ## Manage - `jdx setup --agent cursor --scope system` — Or wire it for every project (user-global config). - `jdx setup --agent cursor --scope project --check` — Report whether it is wired, without writing anything. - `jdx setup --agent cursor --scope project --remove` — Remove the entry again (other entries are never touched). ## Tools - `jdx_show` — Go to declaration: the class card for a type (kind, supertypes, source, provenance). - `jdx_members` — List the members of a type — the '.' completion equivalent. - `jdx_outline` — File Structure: one dense line per member declared on a type (never inherited). - `jdx_body` — Open the method: the source body of one member, sliced verbatim with provenance. - `jdx_source` — Open the file: whole source for a type, or a slice of it, with provenance. - `jdx_signature` — Parameter info: one signature line per overload, with real parameter names. - `jdx_doc` — Quick documentation for a type or member, falling back to the nearest documenting supertype like IntelliJ quick-doc. - `jdx_search` — Search everywhere: symbols by glob, regex or fuzzy name, with did-you-mean. - `jdx_resolve` — What is this symbol? - `jdx_ls` — External library browser: packages in the workspace roots. - `jdx_tree` — External library browser: artifacts and their package trees. - `jdx_usages` — Find usages of a type or member across the workspace jars and source dirs. - `jdx_hierarchy` — Type hierarchy: supertypes upward and subtypes downward across the workspace. - `jdx_implementors` — Who implements this interface or extends this class, transitively. - `jdx_callers` — Call hierarchy in: who calls this method, as a depth-bounded tree. - `jdx_calls` — Call hierarchy out: what this method calls, as a depth-bounded tree. - `jdx_samples` — How is this symbol really used? - `jdx_diff` — What changed between two artifacts? - `jdx_version` — The jdx version this server was built as. - `jdx_doctor` — Environment report. - `jdx_health` — Liveness probe: uptime, workspace served and queries answered. --- # jdx for Kilo Code Give Kilo Code an IDE for Java, Kotlin and JVM jars: wire the jdx MCP server in one command, then ask for members, bodies, usages and call graphs. ## Set up ```console $ curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash $ jdx setup --agent kilo --scope project ``` What it writes: Kilo Code gets the single mcp.jdx entry in kilo.jsonc (project prefers .kilo/). ## Manage - `jdx setup --agent kilo --scope system` — Or wire it for every project (user-global config). - `jdx setup --agent kilo --scope project --check` — Report whether it is wired, without writing anything. - `jdx setup --agent kilo --scope project --remove` — Remove the entry again (other entries are never touched). ## Tools - `jdx_show` — Go to declaration: the class card for a type (kind, supertypes, source, provenance). - `jdx_members` — List the members of a type — the '.' completion equivalent. - `jdx_outline` — File Structure: one dense line per member declared on a type (never inherited). - `jdx_body` — Open the method: the source body of one member, sliced verbatim with provenance. - `jdx_source` — Open the file: whole source for a type, or a slice of it, with provenance. - `jdx_signature` — Parameter info: one signature line per overload, with real parameter names. - `jdx_doc` — Quick documentation for a type or member, falling back to the nearest documenting supertype like IntelliJ quick-doc. - `jdx_search` — Search everywhere: symbols by glob, regex or fuzzy name, with did-you-mean. - `jdx_resolve` — What is this symbol? - `jdx_ls` — External library browser: packages in the workspace roots. - `jdx_tree` — External library browser: artifacts and their package trees. - `jdx_usages` — Find usages of a type or member across the workspace jars and source dirs. - `jdx_hierarchy` — Type hierarchy: supertypes upward and subtypes downward across the workspace. - `jdx_implementors` — Who implements this interface or extends this class, transitively. - `jdx_callers` — Call hierarchy in: who calls this method, as a depth-bounded tree. - `jdx_calls` — Call hierarchy out: what this method calls, as a depth-bounded tree. - `jdx_samples` — How is this symbol really used? - `jdx_diff` — What changed between two artifacts? - `jdx_version` — The jdx version this server was built as. - `jdx_doctor` — Environment report. - `jdx_health` — Liveness probe: uptime, workspace served and queries answered. --- # jdx for Cline Give Cline an IDE for Java, Kotlin and JVM jars: wire the jdx MCP server in one command, then ask for members, bodies, usages and call graphs. ## Set up ```console $ curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash $ jdx setup --agent cline --scope project ``` What it writes: Cline writes the mcpServers.jdx transport entry into the single global cline_mcp_settings.json for either scope (Cline keeps no project-level MCP file). ## Manage - `jdx setup --agent cline --scope system` — Or wire it for every project (user-global config). - `jdx setup --agent cline --scope project --check` — Report whether it is wired, without writing anything. - `jdx setup --agent cline --scope project --remove` — Remove the entry again (other entries are never touched). ## Tools - `jdx_show` — Go to declaration: the class card for a type (kind, supertypes, source, provenance). - `jdx_members` — List the members of a type — the '.' completion equivalent. - `jdx_outline` — File Structure: one dense line per member declared on a type (never inherited). - `jdx_body` — Open the method: the source body of one member, sliced verbatim with provenance. - `jdx_source` — Open the file: whole source for a type, or a slice of it, with provenance. - `jdx_signature` — Parameter info: one signature line per overload, with real parameter names. - `jdx_doc` — Quick documentation for a type or member, falling back to the nearest documenting supertype like IntelliJ quick-doc. - `jdx_search` — Search everywhere: symbols by glob, regex or fuzzy name, with did-you-mean. - `jdx_resolve` — What is this symbol? - `jdx_ls` — External library browser: packages in the workspace roots. - `jdx_tree` — External library browser: artifacts and their package trees. - `jdx_usages` — Find usages of a type or member across the workspace jars and source dirs. - `jdx_hierarchy` — Type hierarchy: supertypes upward and subtypes downward across the workspace. - `jdx_implementors` — Who implements this interface or extends this class, transitively. - `jdx_callers` — Call hierarchy in: who calls this method, as a depth-bounded tree. - `jdx_calls` — Call hierarchy out: what this method calls, as a depth-bounded tree. - `jdx_samples` — How is this symbol really used? - `jdx_diff` — What changed between two artifacts? - `jdx_version` — The jdx version this server was built as. - `jdx_doctor` — Environment report. - `jdx_health` — Liveness probe: uptime, workspace served and queries answered. --- # jdx for Codex CLI Give Codex CLI an IDE for Java, Kotlin and JVM jars: wire the jdx MCP server in one command, then ask for members, bodies, usages and call graphs. ## Set up ```console $ curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash $ jdx setup --agent codex --scope project ``` What it writes: Codex CLI gets the [mcp_servers.jdx] table in .codex/config.toml (project) or ~/.codex/config.toml (system, $CODEX_HOME honoured). ## Manage - `jdx setup --agent codex --scope system` — Or wire it for every project (user-global config). - `jdx setup --agent codex --scope project --check` — Report whether it is wired, without writing anything. - `jdx setup --agent codex --scope project --remove` — Remove the entry again (other entries are never touched). ## Tools - `jdx_show` — Go to declaration: the class card for a type (kind, supertypes, source, provenance). - `jdx_members` — List the members of a type — the '.' completion equivalent. - `jdx_outline` — File Structure: one dense line per member declared on a type (never inherited). - `jdx_body` — Open the method: the source body of one member, sliced verbatim with provenance. - `jdx_source` — Open the file: whole source for a type, or a slice of it, with provenance. - `jdx_signature` — Parameter info: one signature line per overload, with real parameter names. - `jdx_doc` — Quick documentation for a type or member, falling back to the nearest documenting supertype like IntelliJ quick-doc. - `jdx_search` — Search everywhere: symbols by glob, regex or fuzzy name, with did-you-mean. - `jdx_resolve` — What is this symbol? - `jdx_ls` — External library browser: packages in the workspace roots. - `jdx_tree` — External library browser: artifacts and their package trees. - `jdx_usages` — Find usages of a type or member across the workspace jars and source dirs. - `jdx_hierarchy` — Type hierarchy: supertypes upward and subtypes downward across the workspace. - `jdx_implementors` — Who implements this interface or extends this class, transitively. - `jdx_callers` — Call hierarchy in: who calls this method, as a depth-bounded tree. - `jdx_calls` — Call hierarchy out: what this method calls, as a depth-bounded tree. - `jdx_samples` — How is this symbol really used? - `jdx_diff` — What changed between two artifacts? - `jdx_version` — The jdx version this server was built as. - `jdx_doctor` — Environment report. - `jdx_health` — Liveness probe: uptime, workspace served and queries answered. --- # jdx for GitHub Copilot CLI Give GitHub Copilot CLI an IDE for Java, Kotlin and JVM jars: wire the jdx MCP server in one command, then ask for members, bodies, usages and call graphs. ## Set up ```console $ curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash $ jdx setup --agent copilot --scope project ``` What it writes: GitHub Copilot CLI gets the mcpServers.jdx entry in .mcp.json (project) or ~/.copilot/mcp-config.json (system, $COPILOT_HOME honoured). ## Manage - `jdx setup --agent copilot --scope system` — Or wire it for every project (user-global config). - `jdx setup --agent copilot --scope project --check` — Report whether it is wired, without writing anything. - `jdx setup --agent copilot --scope project --remove` — Remove the entry again (other entries are never touched). ## Tools - `jdx_show` — Go to declaration: the class card for a type (kind, supertypes, source, provenance). - `jdx_members` — List the members of a type — the '.' completion equivalent. - `jdx_outline` — File Structure: one dense line per member declared on a type (never inherited). - `jdx_body` — Open the method: the source body of one member, sliced verbatim with provenance. - `jdx_source` — Open the file: whole source for a type, or a slice of it, with provenance. - `jdx_signature` — Parameter info: one signature line per overload, with real parameter names. - `jdx_doc` — Quick documentation for a type or member, falling back to the nearest documenting supertype like IntelliJ quick-doc. - `jdx_search` — Search everywhere: symbols by glob, regex or fuzzy name, with did-you-mean. - `jdx_resolve` — What is this symbol? - `jdx_ls` — External library browser: packages in the workspace roots. - `jdx_tree` — External library browser: artifacts and their package trees. - `jdx_usages` — Find usages of a type or member across the workspace jars and source dirs. - `jdx_hierarchy` — Type hierarchy: supertypes upward and subtypes downward across the workspace. - `jdx_implementors` — Who implements this interface or extends this class, transitively. - `jdx_callers` — Call hierarchy in: who calls this method, as a depth-bounded tree. - `jdx_calls` — Call hierarchy out: what this method calls, as a depth-bounded tree. - `jdx_samples` — How is this symbol really used? - `jdx_diff` — What changed between two artifacts? - `jdx_version` — The jdx version this server was built as. - `jdx_doctor` — Environment report. - `jdx_health` — Liveness probe: uptime, workspace served and queries answered. --- # jdx for Antigravity Give Antigravity an IDE for Java, Kotlin and JVM jars: wire the jdx MCP server in one command, then ask for members, bodies, usages and call graphs. ## Set up ```console $ curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash $ jdx setup --agent antigravity --scope project ``` What it writes: Antigravity gets the mcpServers.jdx entry in .agents/mcp_config.json (project) or ~/.gemini/config/mcp_config.json (system). ## Manage - `jdx setup --agent antigravity --scope system` — Or wire it for every project (user-global config). - `jdx setup --agent antigravity --scope project --check` — Report whether it is wired, without writing anything. - `jdx setup --agent antigravity --scope project --remove` — Remove the entry again (other entries are never touched). ## Tools - `jdx_show` — Go to declaration: the class card for a type (kind, supertypes, source, provenance). - `jdx_members` — List the members of a type — the '.' completion equivalent. - `jdx_outline` — File Structure: one dense line per member declared on a type (never inherited). - `jdx_body` — Open the method: the source body of one member, sliced verbatim with provenance. - `jdx_source` — Open the file: whole source for a type, or a slice of it, with provenance. - `jdx_signature` — Parameter info: one signature line per overload, with real parameter names. - `jdx_doc` — Quick documentation for a type or member, falling back to the nearest documenting supertype like IntelliJ quick-doc. - `jdx_search` — Search everywhere: symbols by glob, regex or fuzzy name, with did-you-mean. - `jdx_resolve` — What is this symbol? - `jdx_ls` — External library browser: packages in the workspace roots. - `jdx_tree` — External library browser: artifacts and their package trees. - `jdx_usages` — Find usages of a type or member across the workspace jars and source dirs. - `jdx_hierarchy` — Type hierarchy: supertypes upward and subtypes downward across the workspace. - `jdx_implementors` — Who implements this interface or extends this class, transitively. - `jdx_callers` — Call hierarchy in: who calls this method, as a depth-bounded tree. - `jdx_calls` — Call hierarchy out: what this method calls, as a depth-bounded tree. - `jdx_samples` — How is this symbol really used? - `jdx_diff` — What changed between two artifacts? - `jdx_version` — The jdx version this server was built as. - `jdx_doctor` — Environment report. - `jdx_health` — Liveness probe: uptime, workspace served and queries answered. --- # Frequently asked questions Short answers about what jdx is, how it compares to the tools agents use today, and what it needs. Each links to the docs with the details. ## What is jdx? jdx is an IDE for AI agents, as a command line tool. It answers the questions an IDE answers for a human — what members a class has, what a method does, who calls it, what implements an interface, what an upgrade breaks — for any JVM jar, Maven coordinate, class or source directory and the JDK. It ships as a CLI and as an MCP server. See the [introduction](https://mohammadmd1383.github.io/jdx/docs/introduction/). ## Which AI agents does it work with? Any agent that speaks the Model Context Protocol or can run shell commands. `jdx setup` wires it into Claude Code, Cursor, Codex CLI, GitHub Copilot CLI, Antigravity, OpenCode, Kilo Code and Cline with one command; other MCP clients take a one-line config. See [Use with AI agents](https://mohammadmd1383.github.io/jdx/docs/ai-agents/). ## How is it different from javap, unzip and grep? Those tools answer one low-level question each, so an agent chains several of them and reads large outputs into its context. jdx answers the actual question in one command: only the method you asked for, inherited members resolved transitively with generics, usages across every jar, real source when a sources jar exists, and a labelled decompilation when it does not. On a measured task it used 2 calls and about a quarter of the bytes. See the [README benchmark](https://github.com/MohammadMD1383/jdx#readme). ## Does it run code from the jars it inspects? No. jdx parses bytecode with ASM and never loads inspected classes, so no static initialiser or any other code from those jars ever runs. It also never writes outside its own cache, config and runtime directories. ## Does it need the library's sources? No. With a `-sources.jar` (or a source directory, or the JDK's `src.zip`) it shows real source. Without one it decompiles with Vineflower and marks the result as reconstructed, and `--engine javap` shows raw bytecode. Structure always comes from bytecode, so member lists and signatures are exact either way. ## Does it support Kotlin? Yes. Kotlin binaries are read through their `@Metadata`, so jdx shows real Kotlin declarations — `suspend` functions, properties, default arguments, `@JvmName` mappings. Reading Kotlin sources needs a one-time `jdx kotlin install`. See [Reading code](https://mohammadmd1383.github.io/jdx/docs/reading-code/#kotlin). ## Does it work on my Gradle or Maven project? Yes. Run it inside the project and it discovers the project's sources and dependency jars from the Gradle and Maven caches, without running Gradle or Maven. You can also name a classpath once as a workspace. See [Classpath \& workspaces](https://mohammadmd1383.github.io/jdx/docs/classpath/). ## Does it access the network? Only when you pass `--fetch`, to download a Maven artifact that is not in your local caches, and when you run `jdx upgrade` or `jdx kotlin install`. Everything else is local. ## How fast is it? About 250 ms for a cold one-shot query and about 20 ms when the daemon or MCP server keeps the index warm. The index is persistent and keyed by content hash, so unchanged jars are never re-read. See [MCP, daemon, HTTP \& batch](https://mohammadmd1383.github.io/jdx/docs/serving/#performance). ## What does it need to run? A Java 21 or later runtime on Linux, macOS or Windows. Install is per user and needs no root. See [Installation](https://mohammadmd1383.github.io/jdx/docs/installation/). ## Is it free? Yes. jdx is open source under the Apache-2.0 license, and every bundled dependency uses a permissive license (Apache-2.0, BSD or MIT). ## How do I report a bug or ask for a feature? Open an issue on [GitHub](https://github.com/MohammadMD1383/jdx/issues) with the exact command, what you expected and what you got. `jdx doctor` output helps with environment problems. --- # Changelog Upgrade with `jdx upgrade`. Unreleased changes since v1.6.1: https://github.com/MohammadMD1383/jdx/compare/v1.6.1...main ## v1.6.1 (2026-10-12) ## jdx v1.6.1 — `usages --kind throw` says what it means Patch release: no behaviour change, no output shape change, no exit code change. `jdx usages --kind throw` has always read `throws` declarations (declaration-level — "who declares this type"), never `ATHROW` throw sites ("who actually throws it"). Attributing a throw site needs data-flow analysis the reference extractor deliberately skips. Until now that took source-diving to learn; now the help text, the MCP tool description, and the docs say it everywhere (closes [#7](https://github.com/MohammadMD1383/jdx/issues/7)). ### Install ```bash curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash ``` Windows users who prefer an Explorer-first bundle: download `jdx-1.6.1-windows.zip` from the assets below and run `sh install-release.sh --tarball jdx-1.6.1-windows.zip`. Release installs self-update with `jdx upgrade`. > Releases are **unsigned**: expect a first-run warning — `Unblock-File` on > Windows (PowerShell), `xattr -d com.apple.quarantine /jdx` on > macOS. ### What changed `throw` = `throws` declarations, not `ATHROW` sites — stated explicitly in: - `jdx usages --help` and the `--kind` flag help (`UsagesCommand`) - the MCP `usages` tool `kind` parameter description - `docs/COMMANDS.md` (command table), `docs/PROPOSAL.md` (§7.3), `docs/guide/navigating-code.md` (kind table + Limitations bullet) - KDoc on `JdxService` (`UsageKindFilter`, T-075 comment, `collectMetadataUsages`), `ReferenceExtractor` (`ATHROW` intentionally not extracted), `RpcDispatch` (usages `kind` contract) The follow-up commit also fixes the guide naming a nonexistent `jdx flow`: throw-site attribution is deferred to future work without naming a command that does not exist yet. ### Known limitations - `usages --kind throw` remains declaration-level: methods whose `throws` clause declares the type. Throw-site (`ATHROW`) attribution needs data-flow analysis and is deferred to future work. - Same standing notes as [v1.6.0](https://github.com/MohammadMD1383/jdx/releases/tag/v1.6.0): two setup backends still missing (Windsurf [#38](https://github.com/MohammadMD1383/jdx/issues/38), Aider [#41](https://github.com/MohammadMD1383/jdx/issues/41)); version-probe caveats [#84](https://github.com/MohammadMD1383/jdx/issues/84) / [#85](https://github.com/MohammadMD1383/jdx/issues/85); `MULTI_RELEASE_VARIANT` provenance [#29](https://github.com/MohammadMD1383/jdx/issues/29). ### Testing - Existing `UsagesServiceTest.kind throw shows throws declarations` already pins the semantics; `UsagesServiceTest` (34 passed), `UsagesCommandTest` (6 passed), `lint` clean (per PR #93). - Release gate: full `./gradlew check -Pci` plus per-OS distribution builds and launcher smoke tests, all green. ### PRs in this release | PR | What | |---|---| | [#93](https://github.com/MohammadMD1383/jdx/pull/93) | Clarify throw as throws declarations (closes #7) | **Full Changelog**: https://github.com/MohammadMD1383/jdx/compare/v1.6.0...v1.6.1 ## v1.6.0 (2026-10-08) ## jdx v1.6.0 — docs website live, Antigravity setup, sturdier `body` Minor release: the documentation website is live (generated from the docs and the real CLI, with gates that stop it drifting), `jdx setup` grew its **eighth** agent backend (Antigravity, verified against a real install), and `jdx body` no longer gives up on one bad Vineflower token. No query command changes behaviour, no output shape changes, and no command exit code changes — if you script against jdx, nothing here needs re-learning. ### Install ```bash curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash ``` Windows users who prefer an Explorer-first bundle: download `jdx-1.6.0-windows.zip` from the assets below and run `sh install-release.sh --tarball jdx-1.6.0-windows.zip`. Release installs self-update with `jdx upgrade`. > Releases are **unsigned**: expect a first-run warning — `Unblock-File` on > Windows (PowerShell), `xattr -d com.apple.quarantine /jdx` on > macOS. ### Docs website is live The GitHub Pages site ([mohammadmd1383.github.io/jdx](https://mohammadmd1383.github.io/jdx/)) is generated from the repository's Markdown files and the freshly built `jdx` binary by a new build-time-only `:site` Gradle module — never shipped in the product distribution. What keeps it honest, on every PR: - **Exec blocks**: `console exec` code blocks in the docs are executed against the built binary and their real output is embedded — if an exit code or an output line changes, the build fails instead of publishing stale docs. - **Doc lint**: every `jdx --flag` mention in the docs is validated against the real CLI's `--help`, so a renamed flag breaks the build, not a user's copy-paste. - **Link checker**: all internal links and anchors must resolve. - **Autogenerated reference**: the command reference pages are rendered from actual help output, and every HTML page gets a Markdown twin for language-model consumption. Riding along in the same PR: layered user guides and FAQ, the green accent colour, release-note heading nesting without skipped levels, and Lighthouse as a warn-only gate that never blocks deploy. ### `jdx setup --agent antigravity` The eighth backend, verified against the real `agy` 1.3.0 binary (installed via the official installer — not just the docs): | `--agent` | Project scope | System scope | Version probe | |---|---|---|---| | `antigravity` | `.agents/mcp_config.json`, walk-up | `~/.gemini/config/mcp_config.json` | `agy --version` | The entry is the minimal shape `agy mcp list` recognises: ```json { "mcpServers": { "jdx": { "command": "jdx", "args": ["mcp"] } } } ``` ```console $ jdx setup --agent antigravity --scope project antigravity project setup installed (mcpServers entry) /home/you/project/.agents/mcp_config.json next: restart antigravity (config loads once at startup) ``` Real-binary findings: system scope writes the user-level file only (all of `agy mcp add`/`list`/`enable`/`disable`/`remove` operate there); `agy mcp` never writes the project file, so jdx hand-edits it in the same `mcpServers` shape. Bidirectional interop was proven with the built jdx both ways — jdx-written files pass `agy mcp list`, `agy`-written bytes pass `jdx --check` (exit 0, byte-identical no-op) — and `jdx --remove` leaves `agy mcp list` empty. Same merge discipline as the other seven backends: unrelated servers survive, re-runs are byte-exact no-ops, `--check` reports without writing (exit 1 when absent). ### Fix: `jdx body` survives one bad Vineflower token (#87) `body` / `source --around` locate members by parsing decompiled text with JavaParser. A single rejected token — Vineflower emitting `import foo.1;` / `this.1` for synthetic `$1` siblings (`Found ".1" expected "." ";"`) — failed the whole file, so a member that `members` lists and `javap` proves was reported **not found**, while `source` served the same file fine unparsed. The fix adds a lenient seam (`parseJavaUnitLenient` / `loadJavaUnitLenient` / `findJavaBodiesLenient`): the offending `Found "..."` token is blanked to spaces — line breaks and lengths preserved, so ranges still address the original lines and slices stay verbatim — and parsing retried, up to 10x. Paired `-sources.jar` input stays strict (still an honest `ParseError` / exit 5). If even lenient parsing fails, the error now says the member exists in bytecode and names the file view that still works (`jdx source 'TYPE' --engine vineflower`) alongside the `--engine javap` hatch. Exit codes are unchanged, so the default-ladder retry still works. ### Test infrastructure: categories, budgets, progress Also in this release: tests are categorised, per-test time budgets are enforced (`test` stays a fast unit loop), and the runner logs live progress — so a slow or hung suite points at the culprit instead of a silent minute. ### Known limitations - **Two agents from the umbrella are still missing**: Windsurf ([#38](https://github.com/MohammadMD1383/jdx/issues/38)) and Aider ([#41](https://github.com/MohammadMD1383/jdx/issues/41)), tracked under [#32](https://github.com/MohammadMD1383/jdx/issues/32). Antigravity (#34) closed with this release. - **Version detection is a best-effort probe**: the raw probe output is **not** collapsed to one line, so a chatty binary spills a multi-line, absolute-path-bearing `WARNING` across the `detected:` line in *text* mode ([#84](https://github.com/MohammadMD1383/jdx/issues/84); the `--json` envelope escapes it correctly and is fine); and the agent-config root comes from the JVM's `user.home`, which on Linux comes from `getpwuid()` and therefore **ignores `$HOME`** ([#85](https://github.com/MohammadMD1383/jdx/issues/85)). - Attribution still covers reader warnings; the `MULTI_RELEASE_VARIANT` *provenance* line does not say which variant was selected ([#29](https://github.com/MohammadMD1383/jdx/issues/29)). ### Testing - **Website**: the `pages` workflow builds and validates the site on the PR; `lint`, `:site:test` and `:site:buildSite` green. - **Antigravity**: 20 new `SetupServiceTest` cases (incl. a real-`agy`-bytes golden pin and generative fixed-point families), 9 new `SetupCommandTest` cases, 3 new byte-pinned goldens, 3 new doctor tests — all with fake homes; 545 unit tests, full integration suite, `lint`, `:site:test`, `:site:buildSite` green. - **Lenient slicing**: 8 new `JavaBodiesLenientTest` tests (incl. a property test: injected bad imports still slice via lenient over generated members), plus `BodyServiceTest` (repairable forced-vineflower succeeds exit 0 with vineflower provenance; unrecoverable names the file view + javap in text and JSON) and `SourceServiceTest` (`--around` with one bad token succeeds); `:sources:integrationTest` 140 pass, `:index:check` 651 pass, `:cli:check` green. ### PRs in this release | PR | What | |---|---| | [#89](https://github.com/MohammadMD1383/jdx/pull/89) | GitHub Pages site generated from the docs and the real CLI | | [#90](https://github.com/MohammadMD1383/jdx/pull/90) | `jdx body` survives a bad Vineflower token | | [#91](https://github.com/MohammadMD1383/jdx/pull/91) | Antigravity setup support, agy-verified | **Full Changelog**: https://github.com/MohammadMD1383/jdx/compare/v1.5.0...v1.6.0 ## v1.5.0 (2026-09-28) ## jdx v1.5.0 — `jdx setup`: every agent you actually use Minor release: `jdx setup` grew from **one** agent backend to **seven**, `jdx doctor` gained an AppCDS row, and three defects were fixed — two of which could make jdx *report the wrong thing*, and one of which had a quality gate silently not running at all. No query command changes behaviour, no output shape changes, and no command exit code changes — if you script against jdx, nothing here needs re-learning. `jdx setup --agent opencode` behaves exactly as it did in v1.4.0, byte for byte. In v1.4.0 you could paste an `opencode.json` snippet by hand and jdx would write it correctly. The obvious follow-up question was the one everybody actually has: *which config file does **my** agent read?* This release answers it for the seven agents the project verified against real installs, writes the entry in that agent's own shape, and reports which line it found — rather than guessing a shape and hoping. | `--agent` | Project scope | System scope | Version probe | |---|---|---|---| | `opencode` (v1.4.0) | `opencode.json`/`opencode.jsonc`, walk-up | `~/.config/opencode` | `opencode --version` → v1/v2 line | | `claude-code` (alias `claude`) | `.mcp.json`, walk-up | `~/.claude.json` | `claude --version` | | `cursor` | `.cursor/mcp.json`, walk-up | `~/.cursor/mcp.json` | `cursor-agent --version` (legacy `agent` shim fallback) | | `kilo` | `.kilo/kilo.json[c]`, else `kilo.json[c]`, walk-up (fresh default `.kilo/kilo.json`) | `~/.config/kilo/kilo.json[c]` | none (Kilo has no classified line) | | `cline` | `~/.cline/data/settings/cline_mcp_settings.json` | same single global file | none | | `codex` | `.codex/config.toml`, walk-up | `$CODEX_HOME/config.toml`, else `~/.codex/config.toml` | `codex --version` | | `copilot` | `.mcp.json`, walk-up, `.github/mcp.json` fallback | `$COPILOT_HOME/mcp-config.json`, else `~/.copilot/mcp-config.json` | `copilot --version` | ### Install ```bash curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash ``` Windows users who prefer an Explorer-first bundle: download `jdx-1.5.0-windows.zip` from the assets below and run `sh install-release.sh --tarball jdx-1.5.0-windows.zip`. Release installs self-update with `jdx upgrade`. > Releases are **unsigned**: expect a first-run warning — `Unblock-File` on > Windows (PowerShell), `xattr -d com.apple.quarantine /jdx` on > macOS. ### `jdx setup --agent claude-code` The shape is the one `claude mcp add jdx -- jdx mcp` would write, so an agent that already knows the entry recognises it: ```console $ jdx setup --agent claude-code --scope project claude-code project setup installed (mcpServers entry) /home/you/project/.mcp.json detected: claude-code (2.1.283 (Claude Code)) next: restart claude-code (config loads once at startup) ``` ```json { "mcpServers": { "jdx": { "command": "jdx", "args": ["mcp"] } } } ``` Project scope walks up for an existing `.mcp.json` and writes one in the checkout root if there is none; system scope targets `~/.claude.json`. ### `jdx setup --agent cursor` Same `mcpServers.jdx` shape — Cursor reads it from `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (system), verified against `cursor-agent 2026.09.26-dd393fe` via `agent mcp list`. ### `jdx setup --agent kilo` Kilo Code is an OpenCode fork sharing the `mcp` map shape, so it gets the **single** `mcp.jdx` entry (not OpenCode's v1+v2 pair — there is no second line to cover). Fresh `kilo.json[c]` files carry no `$schema`. Project scope walks up, preferring the `.kilo/` variant at each level — within one directory the order is `.kilo/kilo.jsonc`, `.kilo/kilo.json`, `kilo.jsonc`, `kilo.json` — and a checkout with no Kilo config gets a fresh `.kilo/kilo.json`. Kilo Code and Cline are the two backends that report **no** `detected:` line: neither has a config-line version to classify, and jdx does not invent one. ### `jdx setup --agent cline` Cline's entry is nested one level deeper — it is a *transport* object: ```json { "mcpServers": { "jdx": { "transport": { "type": "stdio", "command": "jdx", "args": ["mcp"] } } } } ``` Verified against the real install (cline CLI 3.0.65 + extension 4.1.21: `resolveMcpSettingsPath`; cline/cline#2418 open): **Cline keeps no project-level MCP file**, so `--scope project` and `--scope system` name the same global `~/.cline/data/settings/cline_mcp_settings.json`. That is the verified layout, not a fallback, so the output says so out loud rather than letting a project-scoped run look checkout-local: ```console $ jdx setup --agent cline --scope project cline project setup installed (global file — Cline keeps no project-level MCP config) /home/you/.cline/data/settings/cline_mcp_settings.json next: restart Cline (config loads once at startup) ``` The probe also accepts the legacy flat `command`/`args` shape, so `--check` and `doctor` report a Cline entry written by an older jdx. ### `jdx setup --agent codex` Codex CLI config is TOML, so the entry is a `[mcp_servers.jdx]` table and the merge is **line-based** rather than a JSON tree rewrite — every other key in `config.toml` survives as written: ```toml [mcp_servers.jdx] command = "jdx" args = ["mcp"] ``` Verified against `codex-cli 0.157.1`. Project scope targets `.codex/config.toml` (Codex only reads it in a *trusted* checkout); system scope honours `$CODEX_HOME`. ### `jdx setup --agent copilot` The GitHub Copilot CLI entry is `mcpServers.jdx` with `"type": "local"`, in `.mcp.json` (project — the nearest one walking up, `.github/mcp.json` as fallback, trusted directories) or `$COPILOT_HOME/mcp-config.json`, else `~/.copilot/mcp-config.json`. Verified against `GitHub Copilot CLI 1.0.88`. A hand-written `"type": "stdio"` entry probes as installed; jdx writes `local`. ### Every backend still merges, never clobbers Unchanged from v1.4.0 and now true for all seven: unrelated servers, models, providers and every unrelated top-level key survive; only the `jdx` entry is added, replaced, or removed. Re-runs are byte-exact no-ops, so comments and formatting survive. `--check` reports without writing (exit 1 when absent), `--remove` uninstalls cleanly, and exit codes are unchanged: **0** installed/removed/present, **1** checked-absent, **3** bad usage, **5** unreadable config. One backend-specific fix worth calling out: `isJdxCommand` accepts Windows shims (`jdx.exe`, `jdx.cmd`, `jdx.bat`) and backslash paths on **every** host, so `--check` and the `doctor` row stay truthful on Windows instead of reporting "not installed" for an entry jdx itself wrote. ### `jdx doctor` grew an `appcds` row The AppCDS archive (`libs/jdx.jsa`, trained per-OS by `:app:installDist`) was invisible to `doctor` — the one thing users asked about when cold start felt slow. It is now a stat-only row (size + mtime against the fat jar, no JVM probe, no network): | State | Reported | |---|---| | present | `ok` — path and size | | absent | `warn` — cold start without AppCDS; rebuild with `./gradlew :app:installDist` | | empty (0 bytes) | `warn` — corrupt or truncated; rebuild | | older than the fat jar | `warn` — stale; rebuild | | unresolvable (dev layout, no jar) | `warn` — ship with `:app:installDist` | Absent or stale is a `warn`, never a `fail`: the archive only affects speed, and `-Xshare:auto` degrades to a plain run. Only the generic probe wrapper turning a thrown exception into `fail` can make this row fail. `install.sh` now also prints a one-line note when it installs from a build with no archive, pointing at this row rather than failing. The `setup` row was widened in the same change: it now reports wiring for **all seven** backends, on both scopes, with each backend's version classification. ### Fix: a stale fat jar can no longer report the wrong version (#68) A versioned `-PjdxVersion` build on top of a default one left **both** `jdx-0.1.0-SNAPSHOT-all.jar` and `jdx-1.3.0-all.jar` in `app/build/libs`. The launcher globbed and took the first, so `jdx version` could report the wrong version — and `jdx upgrade`, which decides what to do from that version, was misled with it. Fixed on both sides: - the `app` build evicts any superseded `jdx-*-all.jar` after writing its own, so `libs` holds exactly one; - the launcher treats *more than one* fat jar as an ambiguous install and **fails loudly, naming every jar it found**, instead of picking one: ```console $ jdx version jdx: multiple jdx fat jars in '/opt/jdx/libs': '/opt/jdx/libs/jdx-1.4.0-all.jar' '/opt/jdx/libs/jdx-1.5.0-all.jar' — rebuild to leave exactly one: ./gradlew :app:clean :app:installDist ``` ### Fix: a stale AppCDS archive is now silent (#54, #22) The archive is keyed to the exact JDK *build*, so on a different vendor or update the JVM rejects it and logged a `[warning][cds]` version-mismatch notice. Output and exit codes were always correct — but the notice **lands on stdout**, where it corrupts piped and `--json` output, and on stderr it reads like a crash for a command that then works fine. Every launcher now passes `-Xlog:cds=off` (after `-Xshare:auto`, before `-XX:SharedArchiveFile`), so the degradation is silent: exit 0, correct output, empty stderr, just without the archive speedup. Pinned end-to-end by a launcher test that feeds a garbage archive and still requires a clean `jdx version`. ### CI: the `core` mutation gate can run again (#66) The `core` ≥ 80 % mutation gate has **never once** run in CI. PIT aborts in its *pre-scan* — before generating a single mutant, with no score produced at all — when the fixture corpus is missing, and on a clean runner it always was. Two independent causes: a PIT minion is a fresh JVM that inherits neither the `test`/`tier2Test` system properties nor a `dependsOn` on `:testfixtures:build`, so it found no corpus. This is worse than no gate: **a gate that cannot run looks exactly like a passing one.** Fixed by wiring both corpus properties (`jdx.fixturesDir`, `jdx.diffFixturesDir`, named once so they cannot drift from the fixture constants) and all four fixture jars into every PIT module, and — the part that stops it recurring — by `verifyPitestWiring`, a cheap assertion **in every `check`** that fails if a PIT module loses either property or a jar dependency. A wiring regression now reads as one clear red line in seconds instead of four confusing PIT aborts twenty minutes in. The same lesson is now written down where it will be read next: a new rule in `AGENTS.md` §7 and `CONTRIBUTING.md` (any precondition a gate needs gets its own assertion in `check`), plus `docs/TESTING.md` §10.1 and a new "ask, then resolve" convention — a missing input is a property of the machine, so helpers expose an availability probe and the suite `assumeTrue`s on it, making absence a visible SKIP rather than a red that points at product code. ### CI: the `/opencode` agent can read CI logs (#36) The job's `permissions:` block gained `actions: read` (a whitelist, not additive), and its `GITHUB_TOKEN` is now exposed to the agent as `GH_TOKEN` — read-only, this repo only, minted per job and dead when the job ends. So an agent debugging a red build reads `gh run view --log` instead of guessing, and never needs a PAT. ### Known limitations - **Three agents from the umbrella are still missing**: Antigravity ([#34](https://github.com/MohammadMD1383/jdx/issues/34)), Windsurf ([#38](https://github.com/MohammadMD1383/jdx/issues/38)) and Aider ([#41](https://github.com/MohammadMD1383/jdx/issues/41)), tracked under [#32](https://github.com/MohammadMD1383/jdx/issues/32). The seam is one enum case plus a merge function; each lands the way these six did, verified against a real install rather than a guess. - **Cline has one global file, so its two scopes are the same file.** A project-scoped Cline run still edits `~/.cline/...`. This is Cline's verified layout, and the output says so — but it is not checkout-local, and pretending otherwise would be the wrong kind of convenience. - **Codex CLI only reads `.codex/config.toml` in a trusted checkout.** jdx writes it either way; Codex decides whether to load it. - **`jdx setup` is still not served by the daemon** and has no MCP tool or HTTP route: it writes to your config, not to a workspace. It is a local maintenance command like `jdx upgrade` and `jdx cache`. - **Version detection is a best-effort probe** and a binary that answers with something unparseable reports the raw output (`claude-code version unknown ()`, `codex not found on PATH`) so a human can judge — jdx never guesses a line. Two caveats found by dogfooding the released binary while writing these notes, both filed and both pre-existing rather than regressions: the raw probe output is **not** collapsed to one line, so a chatty binary spills a multi-line, absolute-path-bearing `WARNING` across the `detected:` line in *text* mode ([#84](https://github.com/MohammadMD1383/jdx/issues/84); the `--json` envelope escapes it correctly and is fine); and the agent-config root comes from the JVM's `user.home`, which on Linux comes from `getpwuid()` and therefore **ignores `$HOME`** ([#85](https://github.com/MohammadMD1383/jdx/issues/85)) — so overriding `HOME` does not redirect `setup`/`doctor`, and there is currently no flag to point them at a different home. - Attribution still covers reader warnings; the `MULTI_RELEASE_VARIANT` *provenance* line does not say which variant was selected ([#29](https://github.com/MohammadMD1383/jdx/issues/29)). - Follow-ups found while doing this, both open: the fixture-corpus wiring is still copy-pasted across seven module build files ([#82](https://github.com/MohammadMD1383/jdx/issues/82)), and `:index:pitest` reports bogus surviving mutants under the parallel `javap` fan-out ([#81](https://github.com/MohammadMD1383/jdx/issues/81)). ### Testing **185 new tests and 18 new goldens**, none of which can touch your real home: - **98 service tests + 49 adapter tests** for the six new backends, covering the merge matrix per backend (fresh install, merge-preserves-unrelated, remove-keeps-others, byte-exact idempotent re-run, corrupt config → 5, project walk-up, `$CODEX_HOME`/`$COPILOT_HOME` overrides, `type: stdio` probed true where `local` is written, Cline's legacy flat shape, Kilo's `.kilo/` preference, hostile command words) and each backend's `--json` payload. - **18 byte-pinned goldens** — fresh-install, merge-preserves and remove-keeps-others for all six new backends. Rewrite with `-Pgolden.update=true`, and read the diff before committing it. - **23 `doctor` tests** covering the new `appcds` row (present, absent, empty, stale, unresolvable, unreadable) and the widened `setup` row, plus **2** Windows probe tests. - **7 launcher/install round-trips in `:app`** running the **real** scripts: the multiple-fat-jar exit 6, the garbage-archive silent-degradation path, the `install.sh` no-archive note, and the Windows `.exe` probe. - **6 fixture-ownership tests** in `testfixtures`/`core` making the resolver-ownership pin path- and source-set-correct, so the corpus the mutation gate now depends on is the *same* corpus the tests read. `userHome`, `projectDir`, `$CODEX_HOME` and `$COPILOT_HOME` are all temp dirs or constructor args, and every version probe is a `ProcessRunner` fake, so the suite never runs a real agent and never reads your real config. ### Docs `docs/COMMANDS.md` (the `jdx setup` and `jdx doctor` rows rewritten) · `docs/PROPOSAL.md` §15 (launcher tuning) + §17.2 (the release-bundle caveat) + Appendix B (the widened `setup` line) · `README.md` (one setup line per agent) · `docs/TESTING.md` §10.1 (the gate-wiring rule) · `AGENTS.md` §7/§11, `CONTRIBUTING.md` and the `cli`, `app`, `testfixtures` module notes. ### PRs in this release | PR | What | |---|---| | [#70](https://github.com/MohammadMD1383/jdx/pull/70) | Kilo Code setup support | | [#71](https://github.com/MohammadMD1383/jdx/pull/71) | Claude Code setup support | | [#72](https://github.com/MohammadMD1383/jdx/pull/72) | Stale fat-jar version bug fixed | | [#73](https://github.com/MohammadMD1383/jdx/pull/73) | AppCDS `doctor` row + `install.sh` note | | [#74](https://github.com/MohammadMD1383/jdx/pull/74) | Stale CDS noise silenced + pinned + docs | | [#77](https://github.com/MohammadMD1383/jdx/pull/77) | Codex CLI setup support | | [#78](https://github.com/MohammadMD1383/jdx/pull/78) | Cline setup support | | [#79](https://github.com/MohammadMD1383/jdx/pull/79) | Cursor setup support | | [#80](https://github.com/MohammadMD1383/jdx/pull/80) | GitHub Copilot CLI setup support | | [#83](https://github.com/MohammadMD1383/jdx/pull/83) | The `core` mutation gate can get a green pre-scan | **Full Changelog**: https://github.com/MohammadMD1383/jdx/compare/v1.4.0...v1.5.0 ## v1.4.0 (2026-09-27) ## jdx v1.4.0 — `jdx setup`: jdx in your agent, in one command Minor release: one new command, one exit-code fix that closes a hole in the contract, one diff provenance fix. No query command changes behaviour and no output shape changes — if you script against jdx, the only thing to re-learn is that a mistyped command now *fails* instead of exiting 0. Two things an agent could not do before: **wire jdx into its own editor's config** (paste a JSON snippet from the README in the right shape for the right OpenCode line, and get `mcp.servers` vs `mcp` right), and **trust that a usage error is an error** — `jdx nosuchcommand` exited `0`, so a typo looked like a successful run, in every release up to v1.3.0. ### Install ```bash curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash ``` Windows users who prefer an Explorer-first bundle: download `jdx-1.4.0-windows.zip` from the assets below and run `sh install-release.sh --tarball jdx-1.4.0-windows.zip`. Release installs self-update with `jdx upgrade`. > Releases are **unsigned**: expect a first-run warning — `Unblock-File` on > Windows (PowerShell), `xattr -d com.apple.quarantine /jdx` on > macOS. ### `jdx setup --agent opencode` One command wires jdx into OpenCode, in the checkout or for the whole machine: ```console $ jdx setup --agent opencode --scope project opencode project setup installed (v1+v2 entries) /home/you/project/opencode.json detected: opencode v2 (opencode v2.0.18) next: restart opencode (config loads once at startup) ``` **Both** OpenCode lines get wired, always, because they disagree about the shape of an MCP entry and only one of them is what a given install actually reads: - **v1** (`opencode 1.x`): servers sit directly under the top-level `mcp` object keyed by name, each `{ "type": "local", "command": ["jdx","mcp"], "enabled": true }`. - **v2** (`0.0.0-next-*` / `0.0.0-beta-*` / `0.0.0-dev-*` / `2.x`): servers sit under `mcp.servers`, and `enabled` is replaced by its inverse `disabled`. So `setup` writes both entries and *reports* the line it detected rather than guessing which one your install reads: ```json { "$schema": "https://opencode.ai/config.json", "mcp": { "jdx": { "type": "local", "command": ["jdx", "mcp"], "enabled": true }, "servers": { "jdx": { "type": "local", "command": ["jdx", "mcp"], "disabled": false } } } } ``` **It merges; it never clobbers.** Every other server in `mcp` and in `mcp.servers`, every model/provider/theme key, and every unrelated top-level key survives — only `mcp.jdx` and `mcp.servers.jdx` are added, replaced, or removed. The file is read leniently (JSONC comments and trailing commas, which is what a real `opencode.jsonc` carries), and a second run is a **byte-exact no-op**, so your comments and formatting survive a re-run: ```console $ jdx setup --agent opencode --scope project opencode project setup already installed (no changes) /home/you/project/opencode.json detected: opencode v2 (opencode v2.0.18) ``` A v1 server entry *literally named* `servers` cannot share the key with the v2 namespace, so the foreign entry wins untouched and only the v1 `jdx` entry is written. Merge discipline beats completeness. ### Flags and exit codes | Flag | Meaning | |---|---| | `--agent ` | Only `opencode` so far; parsed case- and separator-insensitively (`OpenCode`, `open-code`). Other backends follow the same seam — see the limitations. | | `--scope project\|system` | `project` (default): the nearest `opencode.json[c]` walking up from the checkout, else a fresh `opencode.json` there. `system`: the existing `~/.config/opencode/opencode.jsonc` (what a real install carries), else a fresh `opencode.json` in `~/.config/opencode`. | | `--check` | Report without writing anything. Exit 1 when absent, 0 when present — a CI step can assert wiring without changing it. | | `--remove` | Uninstall: deletes both `jdx` entries, drops an emptied `mcp.servers` and then an emptied `mcp`, keeps every other server. Idempotent. | | `--json` | The same information in the standard envelope. | Exits: `0` installed / removed / checked-present, `1` checked-absent, `3` bad usage, `5` an unreadable or corrupt config (the file is named in the message and is never rewritten). ```console $ jdx setup --agent opencode --scope project --check opencode project setup not installed /home/you/project/opencode.json detected: opencode v2 (opencode v2.0.18) next: jdx setup --agent opencode --scope project $ echo $? 1 $ jdx setup --agent cursor usage error: unsupported --agent 'cursor' (only --agent opencode so far) $ echo $? 3 ``` Restart the agent afterwards — it reads its config once at startup. ### `jdx doctor` grew a `setup` row `jdx doctor` now answers "is jdx wired into my agent, and which line is reading it?" without you having to ask: ```console $ jdx doctor jdx doctor jdk: ok (21 (/usr/lib/jvm/java-21-openjdk)) … setup: ok (opencode v2 (opencode v2.0.18): installed (/home/you/project/opencode.json); no system config (/home/you/.config/opencode/opencode.json)) ``` The row is `ok` when either scope is wired and `warn` when neither is — setup is optional, so it never fails — and it names the file it looked at plus the exact command to fix it. The version probe is the same pure seam `setup` uses, so `doctor` and `setup` can never disagree about which line is installed. ### Fix: a usage error is now an error (exit 3) This closed the largest hole in the exit-code contract. Clikt reports a usage error by handing a status code to its own `Context.exitProcess` seam, and clikt-core's default seam is a no-op (the real one lives in the mordant flavor jdx excludes), so every one of these printed a perfectly correct message and **exited 0**: ```console $ jdx members ; echo $? Usage: jdx members [] Error: missing argument 0 # v1.3.0 and earlier — v1.4.0 prints 3 $ jdx nosuchcommand ; echo $? Usage: jdx [] []... Error: no such subcommand nosuchcommand 3 $ jdx members java.lang.String --nope ; echo $? Usage: jdx members [] Error: no such option --nope. (Possible options: --no-daemon, --no-jdk, --no-color, --repo) 3 ``` They now exit **3** — the code `AGENTS.md` §6 has reserved for usage errors since v1.0.0 — while `--help` and `--version` still exit 0 and every service-level code is untouched (3 for a bad flag value, 5 for an unreadable artifact, …). The mapping is 1 → 3, installed once on the root `Context` so every subcommand inherits it; nothing about the message format changed. If a script of yours ran `jdx …` and checked only for `0`, a typo in it now stops the pipeline instead of passing silently. That is the point. ### Fix: a diff warning now names the artifact it came from (#64) Found by dogfooding `jdx diff` on the gson 2.11.0 → 2.14.0 upgrade. gson's nest-marker classes have binary names that start with an empty `$` segment (`com/google/gson/internal/$Gson$Types.class`), which the reader cannot model, so it degrades. With two inputs the warning could not say which side produced it — and "was this class dropped from the old jar, or is it new in the new one" is exactly the question a diff exists to answer: ```console $ jdx diff ~/.gradle/caches/…/gson-2.11.0.jar ~/.gradle/caches/…/gson-2.14.0.jar api diff gson-2.11.0.jar -> gson-2.14.0.jar (public surface) … warning MULTI_RELEASE_VARIANT: in gson-2.11.0.jar: multi-release gson-2.11.0.jar serves version-specific variants for this JDK (21) warning UNNAMEABLE_CLASS: in gson-2.11.0.jar: unnameable class name (empty name segment in [, Gson, Types]): com/google/gson/internal/$Gson$Types.class ``` That single word — `in gson-2.11.0.jar:` — immediately disproved the assumption the report used to hide behind: those classes are in the **old** jar, not the new one. Both sides' warnings are now attributed before they are unioned, and the consequences are deliberate in both directions: - the same unreadable class in two differently-named jars is **two** facts, so it now reports once per side instead of collapsing into one anonymous line; - a self-diff (`jdx diff a.jar a.jar`) still prints one line, because the two attributed messages are then identical. The reader's own warning text is unchanged — it is shared by every command and pinned in other goldens. Only `diff`'s union copies it, so no other command's output moved a byte. ### Known limitations - **Only OpenCode is wired.** `jdx setup --agent cursor|copilot|cline|windsurf| kilo-code|aider|codex` exits 3 naming what *is* supported. The eight follow-ups are filed and tracked ([#36](https://github.com/MohammadMD1383/jdx/issues/36)–[#42](https://github.com/MohammadMD1383/jdx/issues/42)); each is one enum entry plus a merge rule behind the same seam. - **The v1 MCP entry is deprecated upstream** and slated for removal by OpenCode. jdx keeps writing both entries until v1 support is dropped from jdx — a deliberate, temporary compatibility cost, not an oversight. - `jdx setup` is **not** served by the daemon and has no MCP tool or HTTP route: it writes to your config, not to a workspace, and there is nothing for a remote adapter to answer. It is a local maintenance command like `jdx upgrade` and `jdx cache`. - Version detection is a best-effort probe of `opencode` on `PATH` (falling back to the legacy `opencode2` shim only when no `opencode` exists). A binary that answers with something unparseable reports `opencode version unknown ()` so a human can judge — jdx never guesses a line. - Attribution now covers reader warnings; the `MULTI_RELEASE_VARIANT` *provenance* line still does not say which variant was selected, only which side produced the warning. - Unchanged since v1.2.x and still shipped: the per-OS `libs/jdx.jsa` logs `shared archive file version … does not match` noise on a JDK build other than the CI runner's ([#54](https://github.com/MohammadMD1383/jdx/issues/54)) — output and exit codes are correct, you only lose the shared-archive speedup; and the `core` mutation gate still cannot run in CI ([#66](https://github.com/MohammadMD1383/jdx/issues/66)). ### Testing **60 new tests and 3 new goldens**, none of which can touch your real home: - **30 service tests + 12 adapter tests** for `setup` in `cli`, covering the merge matrix (fresh install, v1-only completed, v2-only completed, unrelated servers preserved in *both* maps, a v1 server named `servers` left alone, remove-keeps-others, byte-exact idempotent re-run, JSONC read, corrupt config → 5, project walk-up, system `jsonc` preference, hostile command words) and the `--json` payload. - **7 property-based families** over the two pure functions that can be wrong quietly: version classification and `describeVersion` (generated minors and patches, so `1.x` vs `0.0.0-next|beta|dev|alpha|rc` vs `2.x` vs garbage is never a guess), the binary probe's preference of `opencode` over the legacy shim (with generated PATH noise), and the JSONC stripper — 200 generated strings that must keep comment-like text *inside* strings intact, plus generated comment bodies that must vanish deterministically. - **3 byte-pinned goldens**: fresh install, merge-preserves, remove-keeps-others. Rewrite with `-Pgolden.update=true` — and read the diff before committing it. - **6 `doctor` tests** for the setup row (unwired, v1, v2, the `opencode2` shim, an unparseable version, wired project) and **5 in `JdxCliTest`**. - **4 launcher round-trips in `:app`** that run the **real** launcher script and fat jar: a missing argument, an unknown command and an unknown option each exit 3, plus a service-level usage error that still exits 3 — which pins the fix at the process boundary, not just at the Clikt seam. - **2 `index` tests** for the diff attribution: a corrupt class in both jars is reported once per side with each side named, and a self-diff still reports once. The probe is a `ProcessRunner` fake and every `userHome`/`projectDir` is a temp dir, so the suite never runs `opencode` and never reads your real config. ### Docs `docs/COMMANDS.md` (a `jdx setup` row + the rewritten `jdx doctor` row) · `docs/PROPOSAL.md` Appendix B (per-command highlights) · `README.md` (the one-line OpenCode setup hook) · and the `cli` module notes for `SetupService` and the doctor row. **Full Changelog**: https://github.com/MohammadMD1383/jdx/compare/v1.3.0...v1.4.0 ## v1.3.0 (2026-09-27) ## jdx v1.3.0 — `jdx diff`: public-API diff with binary-compat warnings Minor release: one new command, and nothing that already shipped changes behaviour. `jdx diff` answers the question a dependency upgrade poses — *what changed, and can I still compile and link against it?* — with every difference named by a rule code an agent can filter on, most breaking first. A real upgrade, on the surface a consumer compiles against — this one is clean, so the CI gate passes and the answer is that you can take the upgrade: ```console $ jdx diff gson-2.11.0.jar gson-2.14.0.jar --fail-on breaking api diff gson-2.11.0.jar -> gson-2.14.0.jar (public surface) info TYPE_ADDED com.google.gson.internal.GsonTypes info TYPE_ADDED com.google.gson.internal.bind.JsonElementTypeAdapter info MEMBER_ADDED com.google.gson.FieldNamingStrategy#alternateNames(java.lang.reflect.Field) … 0 breaking · 0 suspicious · 17 informational · 2 types added · 0 types removed fail-on breaking: no breaking changes (exit 0) ``` The same two jars on the **all** surface, where the internal churn a consumer never sees is also reported — 118 breaking findings, which is the point of the flag for refactors and mod/mixin work: ```console $ jdx diff gson-2.11.0.jar gson-2.14.0.jar --visibility all --severity breaking api diff gson-2.11.0.jar -> gson-2.14.0.jar (all surface) breaking TYPE_REMOVED com.google.gson.Gson$1 breaking TYPE_REMOVED com.google.gson.Gson$2 … 118 breaking · 11 suspicious · 101 informational · 32 types added · 29 types removed ``` Both sides may be a jar, a class directory, a glob, or a Maven coordinate (`--fetch` to download; `~/.gradle/caches` and `~/.m2` are used without it). No workspace is needed and no index is built — a diff names its own two artifacts. Warm, two real jars compare in **230–250 ms**. ### Install ```bash curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash ``` Windows users who prefer an Explorer-first bundle: download `jdx-1.3.0-windows.zip` from the assets below and run `sh install-release.sh --tarball jdx-1.3.0-windows.zip`. Release installs self-update with `jdx upgrade`. > Releases are **unsigned**: expect a first-run warning — `Unblock-File` on > Windows (PowerShell), `xattr -d com.apple.quarantine /jdx` on > macOS. ### What `jdx diff` reports **47 rules at three severities** (16 breaking, 11 suspicious, 20 informational). The rule id *is* the `CompatRule` enum name, so a caller filters on a code and never on prose — `MEMBER_REMOVED`, `RETURN_TYPE_CHANGED`, `STATIC_TO_INSTANCE`, `ENUM_CONSTANT_REMOVED`, `KOTLIN_SUSPEND_CHANGED`, and so on. Full set in `docs/PROPOSAL.md` §7.7. A member's identity is its name plus the **erased parameter list**, so a covariant return is one `RETURN_TYPE_CHANGED` rather than a removal plus an addition, and a field's type is part of its identity. Two pairing passes keep the report free of remove/add avalanches: shape (`name` + arity) first, then Kotlin name and Kotlin arity for JVM renames and `suspend` changes. **An ambiguous bucket is never paired** — with two removals of one shape, guessing which addition replaced which is worse than reporting both. **The inheritance check refuses to guess.** A member gone from its type but declared by one of the type's *new* supertypes is `MEMBER_MOVED_TO_SUPERTYPE`, not a removal — the caller's `invokevirtual` still resolves through the chain. When the walk reaches a supertype the artifact does not hold, absence proves nothing, so the removal stays `MEMBER_REMOVED` and the detail names the supertype it could not read. A diff never downgrades a breaking change on a maybe. The universal roots (`java.lang.Object` and friends) are excluded from that walk, because counting `java.lang.Object` as "missing from the artifact" put an "inheritance not checked" caveat on *every* removal in *every* jar. Structure comes from bytecode alone — no sources jar, no decompilation, no classpath — because a diff has no "flesh" to recover. ### Exit codes — one documented exception A comparison that **ran exits 0 whatever it found**. The answer is in the report, not the process status, because "no differences" and "differences that do not matter to you" are the same outcome and only you can tell them apart. `--fail-on breaking` (or `any`) is the CI gate and the only way a successful diff exits non-zero: ```bash jdx diff old.jar new.jar --fail-on breaking # exit 1 when a breaking change is present ``` The verdict is also in the envelope as `result.gate`, so a consumer that cannot read exit codes still gets the answer. This is the single documented exception to the exit-code table in `AGENTS.md` §6 / `PROPOSAL.md` §16; everything else is unchanged (3 for a bad spec or flag, 5 for an unreadable artifact). ### Flags | Flag | Meaning | |---|---| | `--visibility public\|all` | `public` (default) is the surface a consumer compiles against; `all` compares every declared member, for refactors and mod/mixin work. | | `--severity all\|suspicious\|breaking` | Cumulative: `suspicious` = breaking + suspicious. The per-severity tally always describes the whole comparison, never only what was printed. | | `--fail-on none\|breaking\|any` | Turn the report into a process status. | | `--limit ` | Maximum findings shown (default 200); the tail this cuts is by construction the least severe. | | `--include-synthetic` | Compare bridge/synthetic members too, hidden by default. | | `--fetch`, `--repo` | Maven coordinate resolution, as on every other command. | ### All four interfaces CLI, MCP (`jdx_diff`), the daemon wire, and HTTP (`GET /v1/diff?query=&new=`) — the same `JdxService.diff` call, verified byte-identical between the CLI and HTTP adapters. `jdx diff` is deliberately **not forwarded to the daemon**, and there is deliberately **no `--no-daemon` flag**: a daemon warm on one workspace's classpath holds nothing a two-artifact comparison can reuse, and a `--fail-on` verdict has no way back over the v1 wire. A flag that does nothing is a lie, so it is absent rather than inert — `jdx --no-daemon diff a b` parses, `jdx diff --no-daemon a b` does not. The daemon still *serves* `diff`. ### Fixes that shipped with it Found by the new test families, not by inspection — each is a case where the report was confidently wrong: - **A spurious caveat on every removal.** `MEMBER_REMOVED` carried "supertype `java.lang.Object` is not in *new*.jar; inheritance not checked" on *every* removal in *every* jar, because every class extends `java.lang.Object`. The universal roots are now skipped in the inheritance walk, and a private removal skips the walk entirely (JLS §8.2). - **`SUPERTYPE_REMOVED java.lang.Object`.** A class that becomes an interface, or that now names a real superclass, drops `java.lang.Object` from its header — reported as a lost supertype, on a change already reported as `TYPE_KIND_CHANGED` or `SUPERTYPE_ADDED`. - **`FIELD_TYPE_CHANGED` printed its ref twice.** A field's canonical ref carries no type, so the detail read `now: pkg.Type#field` on both sides of the arrow. It now reads `int -> long`, which is the whole news. - **`ANNOTATION_VALUES_CHANGED kotlin.Metadata` on every Kotlin edit.** The compiler's own bookkeeping changed value on every declaration touched, so every Kotlin diff carried a line that is always true, burying the real findings. The internal annotations are now excluded; a JetBrains `@Nullable` is still compared, because that one is real declared API. - **Every degradation warning was printed twice** on a self-diff. - **macOS and Windows could not build the new test fixtures.** The Kotlin plugin contributes a default `src/diffV1/kotlin` for source set `diffV1`; an *added* `src/diffv1/kotlin` is a different path on Linux and the *same* directory on a case-insensitive filesystem, so every fixture reached the compiler twice (`CLASSIFIER_REDECLARATION`). Fixed with `setSrcDirs` and source-set-cased directory names. - **Windows could not run the glob tests.** `*` is not a legal character in a Windows path, so four tests that built a glob argument with `Path.resolve("*.jar")` threw before their first assertion. A spec is matched, never opened — a glob is a perfectly good argument. ### Known limitations - A public member that is no longer declared but *was* an override of a universal root's method (`toString`, `equals`, …) still links through that root, and is reported as `MEMBER_REMOVED`. Resolving it needs the roots' member names, which is a table, i.e. a guess — so the case stays conservative and documented on the rule. - A degradation warning does not yet say *which* of the two artifacts produced it ([#64](https://github.com/MohammadMD1383/jdx/issues/64)); a multi-release jar trips `MULTI_RELEASE_VARIANT` for the side that carries it. - `VARARGS_CHANGED` is only reachable from hand-modified bytecode: `javac` gives `f(String)` and `f(String...)` *different* descriptors, so that change is reported as `PARAMETER_TYPE_CHANGED`. Pinned by unit tests and listed as unreachable-from-`javac` in the coverage test, so the gap is deliberate rather than invisible. - Unrelated to `diff`, and **pre-existing in every release so far**: a Clikt-level usage error — a missing argument, an unknown command, an unknown flag — prints the right message but exits **0** ([#65](https://github.com/MohammadMD1383/jdx/issues/65)). Service-level exits are correct. - The `core` mutation gate cannot currently run in CI ([#66](https://github.com/MohammadMD1383/jdx/issues/66)): the PIT pre-scan needs the fixture corpus, which three modules do not hand to the minion JVM, so PIT aborts before mutating anything on a clean checkout. Pre-existing since at least v1.0.0. ### Testing **206 tests and 22 goldens** for this feature alone: 73 per-rule examples and 12 property-based families in `core` (self-identity, total order, determinism, the truncation law, text ⊆ JSON, hostile names), 19 snapshot tests, 21 renderer tests, 44 service/dispatch/rule-coverage/golden tests in `index`, and 37 adapter tests in `cli`. Plus a tier-3 `javap` differential over six real version pairs in the local corpus, which pins two things nothing else can: the snapshot's member sets equal `javap -p -s` **exactly**, and the differ is neither blind to a `javap`-visible change nor noisy on an unchanged class. `core`'s mutation gate is 84.0% module-wide against a ≥ 80% threshold, with the new `core/diff` package at 82.2% — the six remaining gaps in that package were real unexercised branches (the enum-constant access flags, the ambiguous-bucket guard's mirror case, field `Signature`/`ConstantValue`/annotations, a non-`stripAppliesTo` Kotlin view, and each comparator tiebreak), not incidental equivalent mutants. New corpus: `testfixtures/src/diffV1` and `src/diffV2` — two versions of the same package, real `javac` and `kotlinc` output, byte-deterministic across a clean rebuild. It self-describes through a **rule-coverage** test rather than per-type annotations: the property worth pinning belongs to the *pair* — that every rule fires against real compiler output. A rule with no fixture fails the build, and a fixture with no declared expectation fails it too, so neither can grow silently. The Kotlin rules are proven against real `@Metadata` there, which matters because `KOTLIN_SUSPEND_CHANGED` depends on a reader flag (`stripTrailingContinuation`) that nothing else would catch if it stopped being set. ### Docs `docs/PROPOSAL.md` §7.7 (the full rule set and the exit-code reasoning), Appendix B, §16, §21 · `docs/COMMANDS.md` · `docs/TESTING.md` §11.2 · `README.md` · and the `core`, `index`, `cli`, `server` and `testfixtures` module notes. **Full Changelog**: https://github.com/MohammadMD1383/jdx/compare/v1.2.1...v1.3.0 ## v1.2.1 (2026-09-26) ## jdx v1.2.1 — Windows correctness + full CI on all three OSes Patch release: no new commands, no output-shape changes on Linux. Windows users get working `--jars` globs, Kotlin KDoc from CRLF checkouts, and JDK sources; everyone gets the CI suite proving it — full `check` (tests + lint + coverage gates) green on Linux, macOS and Windows on every push. ### Install ```bash curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash ``` Windows users who prefer an Explorer-first bundle: download `jdx-1.2.1-windows.zip` from the assets below and run `sh install-release.sh --tarball jdx-1.2.1-windows.zip`. Release installs self-update with `jdx upgrade`. > Releases are **unsigned**: expect a first-run warning — `Unblock-File` on > Windows (PowerShell), `xattr -d com.apple.quarantine /jdx` on > macOS. ### What's changed since v1.2.0 - **JDK sources resolve** (`body`/`source`/`doc` over `java.*`): JDK 9+ `src.zip` nests files under the module directory (`java.base/java/util/ArrayList.java`), which the source mapping missed — every JDK query errored instead of serving sources on JDKs that ship it. - **Windows `--jars` globs work**: `*?[{` are illegal path characters on Windows, so expansion threw `InvalidPathException` instead of matching; glob roots now split with string math, mismatches exit 5, hits sort deterministically on every filesystem. - **Kotlin KDoc from CRLF checkouts**: the PSI doc association misses every KDoc under CRLF text while bodies still slice — CRLF sources (every Windows `text=auto` checkout) silently lost all Kotlin KDoc. Sources now read LF-pinned, so parse offsets and served text agree on all platforms. - **Windows output spellings**: source-scan mention paths join with `/` (one spelling for agents), directory listings sort by name string (Windows orders case-insensitively), fault JSON names JSON-escaped paths. - **`jdx doctor` Kotlin row** probes the resolved cache root, honouring `JDX_CACHE_DIR` instead of the ambient home. - **Full CI/CD suite** (#55–#61): Linux/macOS/Windows `check` legs with JUnit + coverage uploads, coverage gates riding `check`, nightly soak/bench and weekly mutation tiers, workflow hardening (concurrency, minimal permissions, timeouts) and a release check-gate. CI mode (`-Pci`) keeps slow-runner timing report-only instead of red. **Full Changelog**: https://github.com/MohammadMD1383/jdx/compare/v1.2.0...v1.2.1 ## v1.2.0 (2026-09-26) ## jdx v1.2.0 — full multiplatform support Every OS gets a native bundle in this release: `jdx--.tar.gz` and `jdx--.zip` (`` = `linux`, `macos`, `windows`), each with a `.sha256` checksum and a `jdx.jsa` AppCDS archive trained on that OS (the archive only engages when your JDK build matches the CI JDK — otherwise it logs `[cds]` noise and runs normally; see #54). The bare `jdx-.tar.gz/.zip` aliases are the Linux assets, kept so the installer and `jdx upgrade` default URLs keep working. ### Install ```bash curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash ``` Windows users who prefer an Explorer-first bundle: download `jdx-1.2.0-windows.zip` from the assets below and run `sh install-release.sh --tarball jdx-1.2.0-windows.zip`. Release installs self-update with `jdx upgrade`. > Releases are **unsigned**: expect a first-run warning — `Unblock-File` on > Windows (PowerShell), `xattr -d com.apple.quarantine /jdx` on > macOS. ### What's changed since v1.1.0 - **Windows launcher** (`jdx.bat` + `jdx.ps1`): JDK discovery via `%JAVA_HOME%`, `where java.exe`, registry and `%ProgramFiles%` probes; same flags and exit-6 contract as the POSIX launcher (#46). - **Per-OS directories**: cache/config/daemon-socket/install roots follow the platform table (Linux XDG, macOS `~/Library`, Windows `LOCALAPPDATA`/`APPDATA`); old dot-dir locations migrate automatically; a missing `XDG_RUNTIME_DIR` falls back instead of exiting 3 (#44, #45). - **Daemon starts on Windows/macOS** with portable detach, `sun_path` length guard, start mutual exclusion, and pid-validated stop (#49, #50). - **POSIX hardening for macOS**: no GNU-only flags in the launcher or installers; JDK via `/usr/libexec/java_home` (#47). - **Tool + filesystem correctness on Windows**: `.exe` probes, drive/UNC-safe globs, case-insensitive `.jar`, `USERPROFILE` fallback (#51). - **Per-OS release matrix** with zip bundles, per-leg checksums, and per-OS `jdx.jsa` (#48). - README rework (hook/demo/install/quickstart, command catalogue moved to `docs/COMMANDS.md`) plus reproducible benchmark proof. **Full Changelog**: https://github.com/MohammadMD1383/jdx/compare/v1.1.0...v1.2.0 ## v1.1.0 (2026-09-24) ## What's new: `jdx upgrade` self-update Release installs can now self-update from GitHub Releases — no reinstall script needed: ```bash jdx upgrade --check # report only, never touches the install jdx upgrade # update to the latest release jdx upgrade --version v1.1.0 --json # pinned version, machine-readable ``` The updater resolves the target tag, verifies the SHA-256 checksum, and swaps the release tree with a backup-restore guard. Source builds (no `.jdx-release` marker) are refused with exit 3 — they were never release installs. ### One-time note for v1.0.0 installs v1.0.0 predates the `.jdx-release` marker, so `jdx upgrade` on a v1.0.0 install reports "not a release install". Reinstall once with the script below; from v1.1.0 on, self-update works: ```bash curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash ``` **Full Changelog**: https://github.com/MohammadMD1383/jdx/compare/v1.0.0...v1.1.0 ## v1.0.0 (2026-09-24) ## jdx v1.0.0 — initial release `jdx` is an IDE for AI agents, as a command line tool. It answers the questions agents working on JVM codebases ask dozens of times per task — what members a class has, what a method does, who calls it, what implements an interface — directly from `.jar`, `-sources.jar`, class/source directories, Maven coordinates, and the JDK itself. Every command returns the smallest correct answer with text + `--json` output and stable exit codes. ### Read: declaration, members, structure - `jdx show`, `members`, `outline`, `signature`, `doc`, `body`, `source` - Structure from bytecode (ASM, never class-loaded), flesh from sources; no sources jar → Vineflower decompile (labelled `⚠ reconstructed`) → `javap` fallback. Kotlin signatures from `@Metadata`; Kotlin bodies/KDoc via the `jdx kotlin install` sidecar. ### Search and browse - `jdx search` (incl. camel-hump + fuzzy), `resolve`, `ls`, `tree` ### Graph: usages, hierarchy, calls, samples - `jdx usages` (call/read/write/ref/new/throw/annotation), `hierarchy`, `implementors`, `callers`, `calls`, `samples` — live over bytecode roots, plus `--src` textual scan of project source dirs ### Environment, maintenance, serving - `jdx version`, `doctor`, `ws` (named workspaces + Gradle/Maven auto-discovery), `cache`, `kotlin install`, `help --agent`, `bench` - `jdx daemon` (warm JVM), `mcp` (20 typed tools), `serve` (HTTP/JSON), `batch` (NDJSON) — all byte-identical to `--json` ### Install ```bash curl -fsSL https://raw.githubusercontent.com/MohammadMD1383/jdx/main/install-release.sh | bash jdx members java.util.HashMap --limit 5 ``` Requires JDK 21+. Verify with `jdx version` / `jdx doctor`. **Full Changelog**: https://github.com/MohammadMD1383/jdx/commits/v1.0.0