Documentation menu

jdx command reference

The user-facing catalogue. Every shipped flag must appear in jdx <command> --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 <command> --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 <type> Go to declaration classpath flags only
jdx members <type> Code completion after . --declared / --inherited (default), --kind all|method|field|ctor|property, --access public|protected|package|private|all, --static / --instance, --from <supertype>, --grep <regex>, --include-synthetic, --limit (50), --with-doc, --brief, --max-lines, --sort kind|name|declaring, --view kotlin|jvm
jdx outline <type> 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 <member> Parameter info (Ctrl+P) --include-synthetic, --view kotlin|jvm, --limit (50)
jdx doc <symbol> Quick documentation (Ctrl+Q) --inherited (default) / --no-inherited, --raw, --max-lines (200). Undocumented members fall back to the nearest documenting supertype.
jdx body <member> Open the method --context N, --line-numbers, --max-lines (200), --engine vineflower|javap, --with-doc, --with-signature
jdx source <type> Open the file / decompile --lines A:B, --around '<type>#<member>' + --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 <pattern> 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 <artifact-glob>, --package <glob>, --limit (50). Bare words match case-insensitively or as camel humps (HMap finds HashMap).
jdx resolve <name> "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 <symbol> 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 <artifact-glob>, --limit (50), --src <dir> (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> Type hierarchy (Ctrl+H) --up / --down (default both), --direct, --depth N, --in / --exclude, --limit (50). Member refs exit 3.
jdx implementors <type> Who implements this? Alias for hierarchy --down: --direct, --depth N, --in / --exclude, --limit (50)
jdx callers <member> 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 <member> Call hierarchy out Same as callers, plus --external-only (hide callees in the method's own artifact — dependency view)
jdx samples <symbol> (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 <old> <new> "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.
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=<old>&new=<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 <tag> (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 <name> --jars … --src … --coord … --repo … [--jdk|--no-jdk]; add <name> <root>; use <name> [--clear] sets the default; list / info <name> / remove <name>
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 <cache-dir>/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/<hash>-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/<command>?query=…&<param>=…, 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 <jar|dir|glob> (repeatable, merged in front of the workspace) · --coord group:artifact:version (repeatable; local ~/.gradle/caches + ~/.m2 first) · --repo <url> (repeatable Maven mirror, tried before Central) · --fetch (allow downloads incl. -sources.jar, checksum-verified into <cache-dir>/m2/ (~/.cache/jdx/m2/ on Linux)) · --no-jdk (exclude the running JDK stdlib, included by default via jrt:/ + src.zip) · -w/--workspace <name> (also JDX_WORKSPACE env or jdx ws use default; Gradle/Maven project roots auto-discover from the working directory) · --src <dir> (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 <init>, static initialisers <clinit>.

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

Edit this page on GitHub · View as Markdown · Updated