CLI tool
intoolswetrust/jd-cli avatar
intoolswetrust/jd-cli

jd-cli is a thin command line skin over jd-core, and its fork lineage is the thing to weigh

Command line Java Decompiler

656 stars98 forksJavaGPL-3.0

At a glance

What is it?
intoolswetrust/jd-cli packages the jd-core Java decompiler into a JAR, a Docker image and a Maven library, with output to console, flat directory, structured directory or ZIP. It is GPLv3, the last stable release is from 2021, and the only recent tag is a beta.
Who is it for?
Use jd-cli when you want to decompile JARs from a shell script or a container, since the command line surface, the pattern filter and the Docker image are the reason this wrapper exists. Do not embed jd-cli in a closed source product, because the GPLv3 licence that covers it and the underlying jd-core library imposes copyleft obligations that a command line invocation does not.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A wrapper, and the fork chain behind it

jd-cli is described as a simple command line wrapper around jd-core. That wording matters, because it sets the project's scope precisely: it is not a decompiler with new analysis capabilities, it is the packaging, argument parsing and output handling that turns a library into something you can run in a pipeline.

The lineage is three layers deep, and each layer is a fork. The wrapper points at a fork of jd-core maintained by nbauma109, which itself descends from the original java-decompiler/jd-core project. The wrapper itself was forked from JDCommandLine, originally by betterphp. The README attributes the project in the credits section rather than hiding the history.

That chain has consequences for anyone evaluating this for a long term dependency. The version of jd-core you actually get is whatever the nbauma109 fork provides at the pinned revision, not necessarily upstream java-decompiler. If upstream lands a fix, there is no guarantee it flows into your build.

The repository layout is split into two Maven modules, jd-cli/ and jd-lib/, with a pom.xml at the root, a Dockerfile and a build-tools/ directory. jd-cli holds the command line front end and jd-lib holds the reusable library, which is the part other projects depend on.

The practical audience is anyone who needs to read compiled Java: reverse engineers, support engineers looking at an old JAR, and build pipelines that want source output without opening a GUI.

Output targets: console, flat directory, structured directory, ZIP

The option surface is small and is entirely documented by the built in help text, which is the fastest way to understand what the tool does.

code
Usage: java -jar jd-cli.jar [options] [Files to decompile]
  Options:
    --displayLineNumbers, -n

The output choices are what matter for scripting. --outputConsole, -oc sends results to the system output stream. --outputDir, -od takes a directory path and configures a flat directory output for it. --outputDirStructured, -ods takes a directory path and configures a structured directory output. --outputZipFile, -oz takes a zip file path and configures ZIP output.

Two flags control the shape of the code that comes out. --displayLineNumbers, -n adds line numbers in decompiled classes and defaults to false. --escapeUnicodeCharacters, -eu escapes unicode characters in decompiled classes, also defaulting to false.

Three flags control the run itself. --pattern, -p takes a regular expression that the to-be-decompiled file has to match, and entries that do not match are skipped. --serialProcessing, -sp turns off parallel processing, which is the escape hatch for archives that misbehave under concurrency. --skipResources, -sr skips processing resources entirely.

Logging is adjustable with --logLevel, -g, which accepts ALL, TRACE, DEBUG, INFO, WARN, ERROR or OFF, and defaults to INFO. The tool also has --help, -h and --version, -v.

The pattern flag deserves more attention than it usually gets. It is what turns this from a one shot tool into something you can point at a large dependency tree, since you can restrict work to the classes you actually care about.

The Docker image is distroless and mounts the JAR as a volume

A container distribution is published, and the documented invocation is worth reading closely because it shows how the tool is expected to be used against files you do not want to copy into an image.

bash
docker run -it --rm -v `pwd`:/mnt --user $(id -u):$(id -g) \
  kwart/jd-cli /mnt/file-to-decompile.jar -od /mnt/deccompiled-src

The current directory is mounted at /mnt, the JAR is passed as an absolute path inside the container, and the output directory is written back to the same mount. The --user flag with the host's numeric uid and gid means the files written into the volume belong to you rather than to root, which avoids the classic problem of a container leaving root owned artefacts in a working directory.

The image itself is minimal. The Dockerfile shows the whole build:

dockerfile
FROM gcr.io/distroless/java17-debian12
MAINTAINER Josef (kwart) Cacek <[email protected]>

COPY jd-cli/target/jd-cli.jar /jd-cli.jar
ENTRYPOINT ["/usr/bin/java", "-jar", "/jd-cli.jar"]
CMD ["--help"]

A distroless java17 base means there is no shell in the image, so you cannot cd around and debug interactively. What you get is the JAR and a JVM. The entrypoint runs the decompiler directly and the default command is --help.

One inconsistency to expect: the prose says the decompiled files are stored in the decompiled-src directory, while the command writes to deccompiled-src. The container command is the authoritative one.

The image tag on Docker Hub is kwart/jd-cli, and the Dockerfile in this repository builds the same artefact.

Embedding jd-lib from Maven instead of shelling out

If you are writing a Java tool that needs decompiled source, shelling out to the JAR is the wrong shape. That is what jd-lib exists for, and the documented Maven coordinates are:

xml
<dependency>
    <groupId>com.github.kwart.jd</groupId>
    <artifactId>jd-lib</artifactId>
    <version>${jd-cli.version}</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>${slf4j.version}</version>
</dependency>

The programming model is a small set of interfaces. An input describes where the bytes come from, an output describes where they go, and a decompiler is configured with an options object and then asked to run.

java
public static void main(String[] args) {
    JDInput input = new ZipFileInput("/opt/hazelcast/hazelcast.jar");
    JDOutput output = new DirOutput(new File("/tmp/hz-src"));
    JavaDecompiler decompiler = new JavaDecompiler(new DecompilerOptions() {

        @Override
        public boolean isSkipResources() {
            return true;
        }

        @Override
        public boolean isEscapeUnicodeCharacters() {
            return false;
        }

The options are overridden by subclassing DecompilerOptions, so isSkipResources, isEscapeUnicodeCharacters, isDisplayLineNumbers and isParallelProcessingAllowed all become programmatic choices rather than command line flags. The call at the end is input.decompile(decompiler, output), with the input and output objects supplied by the caller.

This is the real differentiator against using jd-core directly: jd-lib gives you a stable seam with named input and output types, and the version is tracked through the ${jd-cli.version} property in your own build.

Java version requirements split at the 1.3.0 line

The runtime requirement is stated with an explicit break point, and it is the kind of detail that decides whether an upgrade is painless.

A Java runtime is required, with version 17 or newer for jd-cli version 1.3.0 and above, and version 8 or newer for older versions below 1.3.0. In other words, moving from the 1.2.x line to the 1.3.0 line raises your floor from Java 8 to Java 17.

That matters for build agents and CI images, and it interacts with the Docker image, which is based on java17 precisely because of the newer requirement. If your build environment is still on Java 8 or Java 11, the older releases remain usable but you are then on a version whose newest tag is from 2021.

The binaries are distributed through GitHub Releases, with the latest bits linked from the project page. The scripts are named jd-cli.bat for Windows and jd-cli for Linux and Unix, and both simply run the JAR, so nothing platform specific is added beyond the launcher name.

The two ways to consume this project therefore come with different operational costs. The CLI needs a JVM on the machine and a JAR on disk. The Docker image needs a container runtime and no local JVM. The Maven library needs neither at runtime, but it does put jd-lib into your own dependency tree.

GPLv3, a beta tag, and a five year gap in stable releases

The licence is GPLv3, stated in the same terms as the original JD-Core library. That is the deciding fact for many organisations, because GPLv3 is copyleft: linking jd-lib into a distributed product carries obligations that a command line invocation does not.

The distinction matters in practice. Running the Docker image or the JAR as a separate process to read someone else's JAR is a different legal situation from shipping jd-lib inside your own closed source application. This review cannot give legal advice, but the licence choice should be checked by whoever owns that decision before any adoption.

The release history is uneven. The tags are jd-cli-1.3.0-beta-1 from 2025-10-30, jd-cli-1.2.1 from 2021-08-01 and jd-cli-1.2.0 from 2021-03-11. So the last stable release is from 2021 and the only recent artefact is explicitly a beta.

The repository is not archived and the last push was on 2026-10-05, so work is continuing even though the stable line has been quiet for a long time. Reading that combination carefully: the project is alive, the stable version you would install from a release link is old, and the recent activity is on the beta.

For a tool you run occasionally against a JAR, an old stable release is often good enough. For a library you intend to depend on, the beta tag and the forked jd-core underneath it are the two things to verify before you commit.

Editorial conclusion

Use jd-cli when you want to decompile JARs from a shell script or a container, since the command line surface, the pattern filter and the Docker image are the reason this wrapper exists. Do not embed jd-cli in a closed source product, because the GPLv3 licence that covers it and the underlying jd-core library imposes copyleft obligations that a command line invocation does not. Before adopting, download the jd-cli-1.3.0-beta-1 tag rather than the 2021 stable release only if you can accept beta quality, confirm your Java runtime is 17 or newer for the 1.3.0 line, and check whether the fork of jd-core it points at still tracks upstream.

Frequently asked questions

What is jd-cli and what does it wrap?

jd-cli is a command line Java decompiler that wraps jd-core, specifically a fork of jd-core maintained by nbauma109 which itself descends from the original java-decompiler/jd-core project. The wrapper itself was forked from JDCommandLine.

How do I run jd-cli from the command line?

Use the jd-cli.bat script on Windows or the jd-cli script on Linux and Unix, which run the JAR. The usage line is java -jar jd-cli.jar [options] [Files to decompile], and the built in help lists every option.

Can jd-cli decompile into a ZIP file or a directory?

Yes. The options are --outputDir for a flat directory output, --outputDirStructured for a structured directory output, --outputZipFile for ZIP output, and --outputConsole to write to the system output stream.

How do I run jd-cli in Docker?

The published image is kwart/jd-cli, built from a distroless java17 base. The documented run mounts the current directory at /mnt, passes the JAR path inside the container and sets the output directory, and passes the host user id so output files are not owned by root.

Can I use jd-cli as a Maven dependency?

Yes, through the jd-lib module with groupId com.github.kwart.jd and artifactId jd-lib. You construct a JDInput such as ZipFileInput, a JDOutput such as DirOutput, configure a JavaDecompiler with DecompilerOptions and call input.decompile(decompiler, output).

What Java version does jd-cli need?

Version 17 or newer is required for jd-cli 1.3.0 and above, and version 8 or newer for versions below 1.3.0. The Docker image is based on a distroless java17 base.

Official sources

  1. intoolswetrust/jd-cli on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/intoolswetrust-jd-cli.svg)](https://hysenlabs.com/projects/intoolswetrust-jd-cli)

Was this useful?