A custom JavaDoc doclet that generates clean Markdown API docs from Java source and comments. Built for agent-ready context, RAG pipelines, and developer portals, it preserves signatures, params, returns, throws, packages, and class structure.
- JDK 25+ (the doclet uses the
jdk.javadocandjdk.compilermodules) - zb on the
PATHto build the executable JAR — no Maven, Gradle, Ant, or third-party runtime libraries
To use a released build without cloning, run the single-file installjmarkdoc script. It downloads the latest executable JAR into the current directory's zbo/ folder and installs the jmarkdoc launcher into the current directory (with the launcher's classpath rewritten to the absolute JAR path so it runs from anywhere) — or into a directory you pass as an argument:
curl -fsSL https://raw.githubusercontent.com/AdamBien/jMarkDoc/main/installjmarkdoc -o installjmarkdoc
chmod +x installjmarkdoc
./installjmarkdoc # current directory — or: ./installjmarkdoc /usr/local/binThen, with the install directory on your PATH:
jmarkdoc src/main/java target/site/apidocsThe rest of this README covers building from source.
Build the executable JAR with zb:
zb.shThis compiles the sources into zbo/jmarkdoc.jar. A doclet is not directly runnable — it is a callback the javadoc tool loads and invokes — so airhacks.jmarkdoc.boundary.Main wraps it in a launcher that runs the documentation tool in-process with the doclet baked in. Run the JAR with no arguments to document src/main/java into target/site/apidocs (the default Maven JavaDoc output directory):
java -jar zbo/jmarkdoc.jarThe source and output directories are optional positional arguments:
java -jar zbo/jmarkdoc.jar src/example/java target/example-mdThe buildAndRun.sh convenience script chains both steps — it builds with zb, runs the JAR against the bundled example sources, and prints the paths of the generated *.md files:
./buildAndRun.shFor day-to-day use from the project root, the single-file jmarkdoc script (Java 25 source-file mode) wraps the built JAR and takes the same optional arguments:
./jmarkdoc # src/main/java -> target/site/apidocs
./jmarkdoc src/example/java target/example-md
./jmarkdoc -helpIt resolves the doclet from zbo/jmarkdoc.jar on its shebang classpath, so run zb.sh first and invoke it from the project root.
The same zbo/jmarkdoc.jar doubles as a doclet JAR. Point the javadoc tool at it and your sources — for example to plug jMarkDoc into an existing javadoc or Maven build:
javadoc \
-doclet airhacks.jmarkdoc.boundary.MarkdownDoclet \
-docletpath zbo/jmarkdoc.jar \
--output target/site/apidocs \
$(find src/main/java -name '*.java')| Option | Argument | Description |
|---|---|---|
--output |
<directory> |
Directory for the generated Markdown. Defaults to the current directory (.). |
Plug the same JAR into the maven-javadoc-plugin as a custom doclet. Point docletPath at zbo/jmarkdoc.jar, disable the standard doclet options (they do not apply to this doclet), and pass --output as an additional option:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<doclet>airhacks.jmarkdoc.boundary.MarkdownDoclet</doclet>
<docletPath>${project.basedir}/zbo/jmarkdoc.jar</docletPath>
<useStandardDocletOptions>false</useStandardDocletOptions>
<additionalOptions>
<additionalOption>--output</additionalOption>
<additionalOption>${project.build.directory}/site/apidocs</additionalOption>
</additionalOptions>
</configuration>
</plugin>Then generate the Markdown with:
mvn javadoc:javadocWrite doc comments in the modern Markdown JavaDoc syntax (///, JDK 23+ / JEP 467). Standard block tags and the custom contract tags work the same as in classic /** */ comments:
/// Creates a new user account.
///
/// Backed by an in-memory store in this example, but the contract is written
/// so a database-backed implementation could replace it unchanged.
///
/// @param email the account email; must be unique and non-blank
/// @return the created [User]
/// @throws IllegalArgumentException if `email` is blank
/// @precondition `email` is non-null and not already registered
/// @postcondition a new [User] is persisted and returned
/// @idempotency not idempotent — repeated calls create distinct accounts
/// @threadsafety safe for concurrent callers; the store is synchronized
public User create(String email) {
...
}Beyond standard JavaDoc (@param, @return, @throws, @see, ...), jMarkDoc renders an Agent Notes block from nine custom contract tags. Each section appears only when the corresponding tag is present in the source — the renderer never invents content:
| Tag | Section |
|---|---|
@precondition |
Preconditions |
@postcondition |
Postconditions |
@sideeffect |
Side effects |
@idempotency |
Idempotency |
@authorization |
Authorization |
@transactions |
Transactions |
@concurrency |
Concurrency |
@threadsafety |
Thread-safety |
@errorhandling |
Error handling |
See src/example/java/UserService.java for a source file that exercises the full range of supported tags.
A method may carry one or more @requirement tags, each naming a stable EARS requirement id from its capability spec (the /sbce traceability tag). jMarkDoc renders them as a Requirements section, one bullet per tag — the id as inline code, followed by an optional description:
/// @requirement R1.1 create and store a new active account
public User create(String name) { ... }renders as:
#### Requirements
- `R1.1` — create and store a new active accountThe doclet itself stays JDK-only. Tests are zero-dependency zunit scripts under test/ — self-contained void main() sources run directly with java --source 25 against zbo/jmarkdoc.jar, never bundled into it:
zb && zunit