{"_id":"@adamancyzhang/agent-java-debugger","_rev":"3-b1f1df1c30ae1130e147b99352cac5e2","name":"@adamancyzhang/agent-java-debugger","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"@adamancyzhang/agent-java-debugger","version":"0.1.0","keywords":["java","jvm","jdwp","debugger","breakpoint","cli","agent","graphql"],"license":"MIT","_id":"@adamancyzhang/agent-java-debugger@0.1.0","maintainers":[{"name":"adamancyzhang","email":"adamancyzhang@163.com"}],"homepage":"https://github.com/adamancyzhang/agent-java-debugger#readme","bugs":{"url":"https://github.com/adamancyzhang/agent-java-debugger/issues"},"bin":{"agent-java-debugger":"dist/cli.js"},"dist":{"shasum":"5c7efe1d9316c77be23ca99ff5430c705ca41126","tarball":"https://registry.npmjs.org/@adamancyzhang/agent-java-debugger/-/agent-java-debugger-0.1.0.tgz","fileCount":22,"integrity":"sha512-mLisqvCc+A6waSeXt3p9VprGD9WUkrzmixahRqoW0Ki2YXyAcT8a73AxUQMWmfBR9ItqgiOLCj2YwQgK2es/SQ==","signatures":[{"sig":"MEUCICK2PfCOmX7ZLeWUA1RFYmHcknlDW/7k9MR2KWFFd1YyAiEAwc/w/j1Lsa1JF3qj6iH3LofH1CYfHaW6wvBfWhve0/0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":187805},"engines":{"node":">=14"},"gitHead":"3d1ceb59f5a7a86c79d6e0e4113a8233638da4f2","scripts":{"test":"tests/e2e_sample.sh","build":"node scripts/build.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"adamancyzhang","email":"adamancyzhang@163.com"},"repository":{"url":"git+https://github.com/adamancyzhang/agent-java-debugger.git","type":"git"},"_npmVersion":"11.19.0","description":"ajd — a JDWP CLI debugger for remote JVMs: source-mapped breakpoints (global/thread-level/conditional), line stepping, in-memory inspection, gql triggering, agent-friendly JSON output. Zero Python dependencies; requires Python >= 3.10 on the machine that ","directories":{},"_nodeVersion":"22.21.1","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/agent-java-debugger_0.1.0_1788771679860_0.026073180778243277","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@adamancyzhang/agent-java-debugger","version":"0.2.0","keywords":["java","jvm","jdwp","debugger","breakpoint","cli","agent","graphql"],"license":"MIT","_id":"@adamancyzhang/agent-java-debugger@0.2.0","maintainers":[{"name":"adamancyzhang","email":"adamancyzhang@163.com"}],"homepage":"https://github.com/adamancyzhang/agent-java-debugger#readme","bugs":{"url":"https://github.com/adamancyzhang/agent-java-debugger/issues"},"bin":{"agent-java-debugger":"dist/cli.js"},"dist":{"shasum":"9f3d30d204f038f784f19f000c0263b3f09157ae","tarball":"https://registry.npmjs.org/@adamancyzhang/agent-java-debugger/-/agent-java-debugger-0.2.0.tgz","fileCount":23,"integrity":"sha512-r41XSI2eG7qSHKV8TvVANFnc+nlFePKH4ZaQGkg+ZZdRCDGnTBUa4G5KR0YJXidsZoa4RDSI1JCRpG44xi4K9Q==","signatures":[{"sig":"MEQCIErxZa6dry+KlZvjNdYFbM+WhkjVHn4hAJxR72/zIK+VAiB+DXHB9ToYVnY3PacV+5oP51IKi1o9GXSnJOdko3bb5g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":200358},"engines":{"node":">=14"},"gitHead":"567712c13ac13d993a93d64ba4945992d1eb14cb","scripts":{"test":"tests/e2e_sample.sh","build":"node scripts/build.js","prepublishOnly":"npm run build"},"_npmUser":{"name":"adamancyzhang","email":"adamancyzhang@163.com"},"repository":{"url":"git+https://github.com/adamancyzhang/agent-java-debugger.git","type":"git"},"_npmVersion":"11.19.0","description":"ajd — a JDWP CLI debugger for remote JVMs: source-mapped breakpoints (global/thread-level/conditional), line stepping, in-memory inspection, gql triggering, agent-friendly JSON output. Zero Python dependencies; requires Python >= 3.10 on the machine that ","directories":{},"_nodeVersion":"22.21.1","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/agent-java-debugger_0.2.0_1788773439959_0.9872038379713648","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@adamancyzhang/agent-java-debugger","version":"0.2.1","description":"JDWP CLI debugger for remote JVMs — breakpoints, stepping, and in-memory inspection for AI agents (pure Python stdlib; requires Python >= 3.10).","bin":{"agent-java-debugger":"dist/cli.js"},"engines":{"node":">=14"},"repository":{"type":"git","url":"git+https://github.com/adamancyzhang/agent-java-debugger.git"},"license":"MIT","keywords":["java","jvm","jdwp","debugger","breakpoint","cli","agent","graphql"],"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"scripts":{"build":"node scripts/build.js","prepublishOnly":"npm run build","test":"tests/e2e_sample.sh"},"gitHead":"6185799044946189d9337462905b0e43a5f8048d","_id":"@adamancyzhang/agent-java-debugger@0.2.1","bugs":{"url":"https://github.com/adamancyzhang/agent-java-debugger/issues"},"homepage":"https://github.com/adamancyzhang/agent-java-debugger#readme","_nodeVersion":"22.21.1","_npmVersion":"11.19.0","dist":{"integrity":"sha512-ckYfmoihKD6xpnxrc+K1V1kuDNhqW5rdYxGRw9j/27OQJaYBir4YTWZkmdH70835WJxNoEC+0INqofHo+1YNlQ==","shasum":"49109c46cfe82eb5fda4e013c4d11c27aed162a4","tarball":"https://registry.npmjs.org/@adamancyzhang/agent-java-debugger/-/agent-java-debugger-0.2.1.tgz","fileCount":22,"unpackedSize":188825,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIE4wrYKfYXUfm4s8DiA6Ct/5kixuonrfFCrjAVvxDvCuAiBlnRxSDDTkb3QWkhoxrIuNAJgQiYOipyuOZEFPdUHKqQ=="}]},"_npmUser":{"name":"adamancyzhang","email":"adamancyzhang@163.com"},"directories":{},"maintainers":[{"name":"adamancyzhang","email":"adamancyzhang@163.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/agent-java-debugger_0.2.1_1788776802004_0.4281427477991957"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-07T09:01:19.582Z","modified":"2026-09-07T10:26:42.335Z","0.1.0":"2026-09-07T09:01:19.995Z","0.2.0":"2026-09-07T09:30:40.103Z","0.2.1":"2026-09-07T10:26:42.157Z"},"bugs":{"url":"https://github.com/adamancyzhang/agent-java-debugger/issues"},"license":"MIT","homepage":"https://github.com/adamancyzhang/agent-java-debugger#readme","keywords":["java","jvm","jdwp","debugger","breakpoint","cli","agent","graphql"],"repository":{"type":"git","url":"git+https://github.com/adamancyzhang/agent-java-debugger.git"},"description":"JDWP CLI debugger for remote JVMs — breakpoints, stepping, and in-memory inspection for AI agents (pure Python stdlib; requires Python >= 3.10).","maintainers":[{"name":"adamancyzhang","email":"adamancyzhang@163.com"}],"readme":"# agent-java-debugger\n\nA JDWP CLI debugger for remote JVMs — IDE-grade breakpoints, stepping, and in-memory inspection from the terminal. Built for AI agents: stable machine-readable JSON events, trustworthy exit codes, and a stop-hold guard that never leaves request threads frozen.\n\n[![npm version](https://img.shields.io/npm/v/%40adamancyzhang%2Fagent-java-debugger)](https://www.npmjs.com/package/@adamancyzhang/agent-java-debugger)\n[![license](https://img.shields.io/npm/l/%40adamancyzhang%2Fagent-java-debugger)](LICENSE)\n[![python](https://img.shields.io/badge/python-%3E%3D3.10-blue)](package.json)\n\n## What it does\n\n`agent-java-debugger` speaks the JDWP wire protocol directly (pure Python standard library, zero dependencies), so it can attach to **any** JVM that started with a debugging port — no IDE, no agent jar, nothing installed on the target machine:\n\n```\n-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:15555\n```\n\n```bash\nagent-java-debugger attach --port 15555 --json --timeout 60 \\\n  --exec \"bp add --file src/main/java/com/foo/UserService.java --line 42 --condition 'id % 7 == 0' --name every-7th\" \\\n  --exec \"continue\" \\\n  --exec \"locals\" \\\n  --exec \"print user.getName()\"\n```\n\nEach `--exec` runs one command; `--json` emits one JSON document per event/result on stdout. Interactive REPL, source-mapped breakpoints (global / thread-level / conditional / tracepoint / once / per-thread), line stepping, stack frames, locals, recursive object inspection, and expression evaluation with real method calls inside the target JVM.\n\n## Installation\n\n### Global (recommended)\n\n```bash\nnpm install -g @adamancyzhang/agent-java-debugger\n```\n\nThe npm launcher finds Python automatically (`py -3` / `python` on Windows, `python3` elsewhere) and runs the bundled `ajd` package.\n\n### From source\n\n```bash\ngit clone https://github.com/adamancyzhang/agent-java-debugger.git\ncd agent-java-debugger\nbin/ajd attach ...            # POSIX wrapper\npython3 -m ajd attach ...     # direct, PYTHONPATH=. from the repo root\n# or: pip install .\n```\n\n### Requirements\n\n* Python ≥ 3.10 on the machine running the CLI.\n* The debugged JVM must run with the `-agentlib:jdwp=…` flag above (`suspend=y` makes it wait for a debugger before running `main` — deterministic start). The JVM itself needs nothing installed and may live anywhere: any OS, any CPU — JDWP is a TCP wire protocol.\n* The debugger only **observes and inspects** — it never kills the target: `quit` detaches, `ajd run` leaves the launched JVM running.\n\n## Quick Start\n\n```bash\n# interactive REPL\nagent-java-debugger attach --host 127.0.0.1 --port 15555 --source-dir sources/my-app\n\n# scripted, machine-readable (agent mode)\nagent-java-debugger attach --port 15555 --json \\\n  --exec \"bp add --file src/main/java/com/foo/UserService.java --line 42 --condition 'id % 7 == 0'\" \\\n  --exec \"continue\" --exec \"locals\" --exec \"print user.getName()\" --exec \"finish\"\n\n# launch a local JVM with JDWP enabled and attach\nagent-java-debugger run --port 15556 -- -cp out Tick\n```\n\n## Commands\n\n### attach\n\n```\nagent-java-debugger attach [--host H] [--port P] [--source-dir DIR] [--exec CMD]... [--json] [--timeout S] [--cmd-timeout S] [--resume-after S]\n```\n\n| Flag | Description |\n|---|---|\n| `--host` / `--port` | JDWP endpoint (default `127.0.0.1:15555`) |\n| `--source-dir <dir>` | Source tree(s) for file breakpoints and the `source` command (repeatable) |\n| `--exec <cmd>` | Run one command line instead of the REPL (repeatable; `continue`/`step`/`finish` block until the next stop or `--timeout`) |\n| `--json` | Machine-readable output: one JSON document per event/result |\n| `--timeout <s>` | Deadline for waiting on the next stop (default 60) |\n| `--cmd-timeout <s>` | Per-command reply timeout (default 15) |\n| `--resume-after <s>` | Stop-hold guard: auto-resume a held stop after N seconds of inactivity so request threads are never blocked (default 60; `0` disables) |\n\nWithout `--exec` an interactive REPL starts; pipe commands to stdin to script it.\n\n### Breakpoints (bp)\n\n```bash\nbp add --class com.foo.Bar --line 42          # by class (must contain the line)\nbp add --file path/to/Bar.java --line 42      # by source file (inner classes resolved; works before the class loads)\nbp list | bp remove <id|name> | bp enable <id|name> | bp disable <id|name> | bp clear\n```\n\n| Option | Meaning |\n|---|---|\n| `--suspend all` (default) | **global**: suspend every thread on hit |\n| `--suspend thread` | **thread-level**: suspend only the hitting thread (least intrusive on live apps) |\n| `--suspend none` | tracepoint: logs the hit, never suspends |\n| `--condition 'expr'` | **conditional**: evaluated in the hitting thread's top frame *inside the target JVM*; false auto-resumes silently. Requires suspend thread/all |\n| `--thread <id\\|name>` | only fire for one specific thread |\n| `--once` | auto-remove after the first stop it produces |\n| `--name label` | human/agent-friendly id for stop events |\n\nPending breakpoints resolve automatically when their class loads (CLASS_PREPARE) — set them before the class exists and they just work.\n\n### Stepping and the stack\n\n```bash\nn/next        # step over (line)\ns/step        # step into (line)\nfin/finish    # step out of the current method\nbt/frames     # stack of the current thread (class.method(file:line) per frame)\nframe <n> | up | down\nthread <id|name>   # switch threads; threads lists them (◉ = current)\n```\n\n### Memory inspection\n\n```bash\nlocals                     # live variables of the selected frame\np/print <expr>             # evaluate an expression in the target JVM\ninspect <expr> [--depth N] # recursive field tree of an object\nclasses [pattern]          # loaded classes matching a substring\nsource                     # source window around the current line\n```\n\n### gql — fire a GraphQL request (e2e trigger)\n\n```bash\nagent-java-debugger gql --url http://host/api/graphql --query '{ viewer { id } }' --vars '{\"x\": 1}'\n# inside a session:  gql <url> <query> [--vars JSON]\n```\n\nClassic e2e loop: set a breakpoint in a background `attach --exec` run → fire the gql request → the stop lands with the request's stack frame → inspect.\n\n### oinone — gql-driven platform helpers\n\n`agent-java-debugger oinone …` mirrors the frontend GenericFunctionService — generic gql requests with cookie-session persistence (default jar `~/.ajd/cookies.txt`, chmod 0600):\n\n```bash\nagent-java-debugger oinone login --url http://host:8091/pamirs/api --login admin --password admin\nagent-java-debugger oinone count  --url … --model action --rsql \"1==1\"\nagent-java-debugger oinone exec  --url … --model ganttDemoModel --function queryPage \\\n    --args '{\"cond\": {\"name\": \"x\"}, \"page\": {\"pageIndex\": 0, \"pageSize\": 10}}' \\\n    --fields 'id code name' [--mutation]\n```\n\nLogin failures are detected (`errorCode != 0` → stderr + exit 1).\n\n### skills\n\n```bash\nagent-java-debugger skills           # list the main skill + on-demand extensions\nagent-java-debugger skills oinone    # fetch the oinone extension content (offline)\nagent-java-debugger skills get agent-java-debugger   # this skill's full text\n```\n\nAgent integration ships in two parts so that only **one skill** is loaded into the agent context:\n\n* [`skills/agent-java-debugger/SKILL.md`](skills/agent-java-debugger/SKILL.md) — the tool's own skill: breakpoints, stepping, inspection, agent-mode conventions. This is the only skill an agent registers.\n* [`skill-data/`](skill-data/) — on-demand extension content (the [`oinone` companion](skill-data/oinone/SKILL.md): gql protocol, login, pamirs-designer recipes). An agent working on an oinone project fetches it with `agent-java-debugger skills oinone` — it costs no context until needed and works offline.\n\nBoth directories ship verbatim in the npm package (see `files` in package.json); the CLI resolves them from the package root.\n\n## Expression language (conditions, print, inspect)\n\nJava-flavored, evaluated against the suspended frame:\n\n* literals — ints, `long`-suffixed (`1L`), floats/doubles, `'c'`, `\"str\"`, `true/false`, `null`\n* locals, `this`, fields of `this` (walking the superclass chain), statics via `com.foo.Bar.CONST` (longest loaded-class prefix match)\n* instance calls `list.size()`, `s.substring(1)`, bare calls `isEmpty()`, static calls `Math.abs(x)` — really invoked in the target with the suspended thread; overloads resolve by argument tags; a thrown target exception surfaces as an error with its message\n* arrays: `arr.length`, element formatting\n* operators `+ - * / % < <= > >= == != && || !` with Java semantics (binary numeric promotion, integer division truncates, `%` follows the dividend's sign, int/long wraparound); `==` compares primitives numerically, strings by value, objects by identity; `+` concatenates strings\n\n## JSON event reference (agent mode)\n\n```jsonc\n{\"event\":\"attach\",\"host\":\"…\",\"port\":15555,\"vm\":{…},\"classes\":N,\"threads\":M}\n{\"type\":\"command\",\"line\":\"bp add …\"}                 // echo of each --exec\n{\"type\":\"breakpoint\",\"action\":\"add\",\"bp\":{…},\"state\":\"set at 1 location(s)\"}\n{\"event\":\"trace\",\"bp\":{…},\"thread\":\"…\",\"location\":{…}}          // tracepoint\n{\"event\":\"stop\",\"reason\":\"breakpoint|step\",\"thread\":\"main\",\n \"class\":\"com.foo.Bar\",\"method\":\"m\",\"line\":42,\"file\":\"Bar.java\",\n \"bp\":{\"id\":1,\"name\":\"every-7th\"},\n \"source\":[{\"line\":39,\"text\":\"…\"},…]}                 // stop, with source window\n{\"type\":\"locals\",\"thread\":1,\"frame\":0,\"this\":{…},\"vars\":[{\"name\":\"id\",\"tag\":\"I\",\"value\":\"7\",\"raw\":7,\"object_id\":null},…]}\n{\"type\":\"print\",\"expression\":\"user.getName()\",\"tag\":\"s\",\"value\":\"'alice'\"}\n{\"type\":\"inspect\",\"expression\":\"user\",\"depth\":2,\"tree\":[\"com.foo.User@0x21f\",\"  name = 'alice'\",…]}\n{\"type\":\"timeout\",\"event\":\"no_stop\"}                  // wait exceeded\n{\"type\":\"error\",\"message\":\"…\"}\n{\"event\":\"vm_death\"}\n```\n\nAgent-mode conventions:\n\n* **Exit code**: `0` only when every `--exec` command succeeded and no wait timed out; `1` when any command errored or a wait timed out; `2` for usage/connection failures; `130` on Ctrl-C. An agent can trust a non-zero code as \"the script failed\".\n* **Stop-hold guard**: a breakpoint suspends request threads in the target. If no command arrives for `--resume-after` seconds (default 60; `0` disables) the session auto-resumes the VM and reports `{\"type\":\"error\",\"message\":\"stop held for … — VM auto-resumed …\"}` so a forgotten breakpoint can never freeze request threads indefinitely.\n* **Target exceptions in expressions**: a method called by `print`/`inspect` that throws surfaces as an error with the exception text — never as a silent `null`.\n\n## Testing\n\n```bash\ntests/run_sample.sh      # build & start tests/sample/Tick.java on :15556\nPORT=15560 tests/e2e_sample.sh   # 25 scenario assertions in --json mode\ntests/e2e_pamirs.sh      # 7 live-assertions against a pamirs backend on :15555\n```\n\nThe e2e suites cover: class/file breakpoints (including inner classes and pending resolution), global/thread/tracepoint suspensions, conditional breakpoints (true/false/impossible), `--once`, thread filters, stepping, locals, print, inspect, frames, gql login + trigger, target-side invocation, step-out, and JSON output.\n\n## Architecture\n\n```\najd/\n├── jdwp.py        transport: handshake, packet framing, reader thread,\n│                  reply dispatch (error codes live in the reply HEADER)\n├── commands.py    typed wrappers for the JDWP command set + value codec\n├── events.py      composite-event parsing, event kinds, request modifiers\n├── session.py     engine: class/thread caches, stop contexts, frames,\n│                  locals, stepping, resume/suspend, target invocation\n├── breakpoints.py breakpoint model + manager (resolution, pending,\n│                  ghost-hit suppression for clears while stopped)\n├── evalexpr.py    lexer/parser/evaluator for conditions & print\n├── values.py      value formatting and recursive object inspection\n├── sourcemap.py   local source lookup, line windows, package hints\n├── gql.py         GraphQL POST helper\n├── oinone.py      oinone login/exec/count + cookie jar\n├── repl.py        command runner shared by REPL and --exec\n└── cli.py         attach / gql / oinone / run / skills subcommands\n```\n\nDesign notes worth knowing:\n\n* **One consumer thread** owns all event handling; commands are safe to issue from it (never from the reader thread — that would deadlock on its own reply).\n* **Breakpoints removed while the VM is stopped on them** produce one ghost re-fire on resume (JVMTI defers the removal); the engine swallows it silently (also for disabled breakpoints).\n* **`continue`'s wait is bounded by one deadline** across all auto-resumed (condition-false) hits — a never-true condition times out instead of cycling forever.\n* Frame ids are valid only for one stop: the frame cache is dropped on every resume and every new stop.\n* **InvokeMethod packets carry a trailing `options` int (JDWP 1.6+)** — omitting it makes HotSpot return error 113 (INTERNAL) on every invoke. Verified byte-for-byte against jdb via a logging proxy.\n* **InvokeMethod replies carry two values** — the return value AND the thrown exception; reading only the first would silently turn target exceptions into `null`.\n* **A successful invoke renumbers the target thread's frame ids** (the invocation's wrapper frame shifts the stack); the frame cache must be dropped after every invoke or subsequent locals reads come back empty.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}