From java-debugger
Debug Java code by inspecting program state at breakpoints and watchpoints using the bundled DebugInspector tool, instead of adding System.out.println statements or writing throwaway debug programs. Use this skill whenever you need to understand why Java code produces wrong results — especially when test cases fail and you need to observe variable values, object state, or execution flow. Triggers on debugging Java test failures, investigating unexpected behavior, understanding program state, "why does this test fail", "what value does X have", or any situation where you'd otherwise reach for System.out.println or write a temporary main() method to probe values. Even for quick debugging tasks, prefer DebugInspector over modifying source code — it runs in seconds, is non-invasive, and outputs compact one-line summaries by default.
How this skill is triggered — by the user, by Claude, or both
Slash command
/java-debugger:java-debuggerThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
When you need to understand why Java code misbehaves — wrong return values, unexpected nulls, failed assertions — use the bundled `DebugInspector` tool to inspect program state at runtime. It uses Java's Debug Interface (JDI) to set breakpoints, capture local variables, evaluate expressions, and watch field modifications — all in a single command. No code modification needed.
When you need to understand why Java code misbehaves — wrong return values, unexpected nulls, failed assertions — use the bundled DebugInspector tool to inspect program state at runtime. It uses Java's Debug Interface (JDI) to set breakpoints, capture local variables, evaluate expressions, and watch field modifications — all in a single command. No code modification needed.
Adding System.out.println for debugging is tempting but costly: you modify code, recompile, risk leaving debug artifacts behind, and can only see what you thought to print. DebugInspector lets you inspect anything at any point, including things you didn't anticipate.
Use DebugInspector when:
Don't use DebugInspector when:
The tool lives at scripts/DebugInspector.java relative to this skill. It's a single-file Java program that uses JDI (Java Debug Interface) to launch a test in debug mode, attach, set breakpoints/watchpoints, collect state, and return output — all in one command, typically completing in 3-5 seconds.
java ${CLAUDE_SKILL_DIR}/scripts/DebugInspector.java \
--maven-project <project-dir> \
--test "<fully.qualified.TestClass#testMethod>" \
--break "<fully.qualified.ClassName>:<line>" \
--port <unique-port>
java ${CLAUDE_SKILL_DIR}/scripts/DebugInspector.java \
--gradle-project <project-dir> \
--test "<fully.qualified.TestClass#testMethod>" \
--break "<fully.qualified.ClassName>:<line>" \
--port <unique-port>
If the JVM is already running with JDWP enabled:
java ${CLAUDE_SKILL_DIR}/scripts/DebugInspector.java \
--attach \
--port <port> \
--break "<fully.qualified.ClassName>:<line>"
Use a port other than 5005 (e.g., 6001-6099) to avoid conflicts with other debug sessions. Pick a different port for each concurrent debug run.
Most invocations need only: --break "ClassName:line" --eval "expr" --no-locals
Use --format eval when you only care about expression values. Use --format json when you need to inspect object structure. Use --count-only to understand call frequency before setting targeted breakpoints. Use --break-if to avoid output explosion in hot loops. Use --max-hits N if you expect many hits but only need a sample.
The most common pattern. Set a breakpoint at a line of interest and inspect all local variables:
java ${CLAUDE_SKILL_DIR}/scripts/DebugInspector.java \
--maven-project /path/to/project \
--test "com.example.MyTest#testMethod" \
--break "com.example.MyClass:42" \
--port 6001
Output is one compact line per hit showing method, locals, and any eval results. For reference types, object IDs let you detect aliasing (two variables with the same id reference the same object).
Use --eval to evaluate expressions at breakpoints — variable lookups, field access, and no-arg method calls. Combine with --no-locals to keep output focused:
java ${CLAUDE_SKILL_DIR}/scripts/DebugInspector.java \
--maven-project /path/to/project \
--test "com.example.MyTest#testMethod" \
--break "com.example.MyClass:42" \
--eval "myList.size()" \
--eval "myVar.someField" \
--eval "this.fieldName" \
--no-locals \
--port 6001
Supported expression forms:
varname — a local variable (shown in detail)varname.fieldName — field access on a local variablevarname.methodName() — no-arg method call on a local variablea == b — reference identity comparison (returns true/false)this.fieldName — field of the current objectUse --watch to break every time a field is accessed or modified. This is essential for tracking down mutation bugs:
java ${CLAUDE_SKILL_DIR}/scripts/DebugInspector.java \
--maven-project /path/to/project \
--test "com.example.MyTest#testMethod" \
--watch "com.example.MyClass.fieldName" \
--port 6001
The output shows each modification with old and new values, plus the stack trace of who modified it. For inner classes, use $ syntax: com.example.Outer$Inner.field.
Break when a specific exception is thrown to see the state at the throw site:
java ${CLAUDE_SKILL_DIR}/scripts/DebugInspector.java \
--maven-project /path/to/project \
--test "com.example.MyTest#testMethod" \
--catch "java.lang.NullPointerException" \
--port 6001
Set multiple breakpoints to trace execution through a method:
java ${CLAUDE_SKILL_DIR}/scripts/DebugInspector.java \
--maven-project /path/to/project \
--test "com.example.MyTest#testMethod" \
--break "com.example.MyClass:25" \
--break "com.example.MyClass:31" \
--break "com.example.MyClass:35" \
--port 6001
Each breakpoint hit appears as a separate compact line, letting you trace execution through a method at a glance.
Before setting targeted breakpoints in a loop or frequently-called method, use --count-only to understand call frequency without capturing state:
java ${CLAUDE_SKILL_DIR}/scripts/DebugInspector.java \
--maven-project /path/to/project \
--test "com.example.MyTest#testMethod" \
--break "com.example.MyClass:15" \
--break "com.example.MyClass:22" \
--count-only \
--port 6001
Output shows hit counts per location (e.g., com.example.MyClass:15 => 47 hits). Once you know which line fires most, add --eval or --break-if to that specific location in a follow-up run.
Use --break-if to avoid output explosion when a breakpoint fires many times. The condition is evaluated as an expression at that location:
java ${CLAUDE_SKILL_DIR}/scripts/DebugInspector.java \
--maven-project /path/to/project \
--test "com.example.MyTest#testMethod" \
--break-if "com.example.MyClass:30" "index == 5" \
--eval "currentValue" \
--no-locals \
--port 6001
This only captures state when index == 5, even if the line executes hundreds of times. Combine with --count-only to first confirm the condition filters correctly.
For multi-module Maven projects, always pass the path to the specific submodule that contains the test, not the root project directory. This ensures surefire runs in the right module context:
java ${CLAUDE_SKILL_DIR}/scripts/DebugInspector.java \
--maven-project /path/to/project/submodule \
--test "com.example.SubmoduleTest#testMethod" \
--break "com.example.SubmoduleClass:42" \
--port 6001
If you pass the root directory of a multi-module project and the test isn't found, the tool will output a diagnostic listing available submodules. Use that output to identify the correct submodule path.
The tool outputs one line per breakpoint hit (compact format, default):
HIT com.example.MyClass:42 myMethod() | myVar=42, myString="hello" | eval: myList.size()=5
Fields shown:
--no-locals)--eval is used)For watchpoint hits, the format includes old and new values:
WATCH com.example.MyClass.price modified: 10.0 -> 11.0 in setPrice()
For structured output use --format eval (JSONL, one object per line):
{"location":"com.example.MyClass:42","method":"myMethod","eval":{"myList.size()":5}}
For full detail use --format json (JSON array with all fields including nested objects and stack traces):
[
{
"location": "com.example.MyClass:42",
"method": "myMethod",
"stack": ["com.example.MyClass.myMethod:42", "com.example.MyTest.testMethod:15"],
"locals": {
"myVar": 42,
"myObj": {"type": "com.example.Foo", "id": 1832, "fields": {"name": "bar"}}
},
"eval": {"myList.size()": 5}
}
]
Key things to look for in any format:
id reference the same object (aliasing)null values: Variables that shouldn't be null--eval for key expressions--break-if conditions, run againEach DebugInspector invocation takes 3-5 seconds, so iterating is cheap.
| Option | Description |
|---|---|
--maven-project <dir> | Start Maven surefire in debug mode |
--gradle-project <dir> | Start Gradle test in debug mode |
--attach | Attach to an already-running JVM |
--test <class#method> | Test to run (required for maven/gradle mode) |
--break <class:line> | Set breakpoint (repeatable) |
--break-if <class:line> <condition> | Conditional breakpoint (repeatable) |
--watch <class.field> | Watch field access/modification (repeatable) |
--eval <expr> | Evaluate expression at breakpoint (repeatable) |
--catch <exception> | Break when exception is thrown |
--no-locals | Skip dumping local variables |
--port <port> | Debug port (default: 5005, always override this) |
--timeout <seconds> | Max wait time (default: 30) |
--max-depth <n> | Object inspection depth (default: 2) |
--max-elements <n> | Max collection elements to show (default: 10) |
--format <mode> | Output format: compact (default), eval, json |
--stack-depth <n> | Max stack frames after filtering (default: 10, json only) |
--stack-full | Show all stack frames (disable framework filtering) |
--max-hits <n> | Max breakpoint hits to capture (default: 50) |
--count-only | Count hits per location, no state capture |
Port conflicts: Always use --port with a unique value (e.g., 6001-6099). Never use 5005 without checking.
Test not found in multi-module project: Pass the submodule directory, not the root. Run with --maven-project pointed at the root first; the diagnostic output will list available submodules.
Exit codes:
0 — Success, results captured1 — Error (bad arguments, connection failure, test class not found)2 — Timeout — partial results may be present in stdout if any hits were captured before timeoutToo many hits: Use --count-only first to see how often each location fires, then use --break-if or --max-hits to narrow down.
Missing debug info: If locals show as unavailable, the class was compiled without debug info. Check that javac -g or equivalent Maven/Gradle debug flag is set.
Read references/build-tool-debug.md for build-tool-specific debug configuration and additional troubleshooting guidance.
npx claudepluginhub kroepke/claude-marketplace --plugin java-debuggerDebug live Java applications via JDWP with breakpoints, state inspection, expression evaluation, variable mutation, logpoints, and field watchpoints.
Guides debugging Java applications via IntelliJ debugger: breakpoints, stepping, expression evaluation, and runtime state inspection. Includes preflight checks for MCP tool availability.
Debugs Java applications using JDB CLI: attach to running JVMs with JDWP, launch new ones under debugger, set breakpoints, step code, inspect variables/threads, diagnose exceptions.