agent-dbg 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bin/ndbg +0 -0
- package/.claude/settings.local.json +21 -0
- package/.claude/skills/ndbg-debugger/ndbg-debugger/SKILL.md +116 -0
- package/.claude/skills/ndbg-debugger/ndbg-debugger/references/commands.md +173 -0
- package/CLAUDE.md +43 -0
- package/PROGRESS.md +261 -0
- package/README.md +67 -0
- package/biome.json +41 -0
- package/ndbg-spec.md +958 -0
- package/package.json +30 -0
- package/src/cdp/client.ts +198 -0
- package/src/cdp/types.ts +16 -0
- package/src/cli/parser.ts +287 -0
- package/src/cli/registry.ts +7 -0
- package/src/cli/types.ts +24 -0
- package/src/commands/attach.ts +47 -0
- package/src/commands/blackbox-ls.ts +38 -0
- package/src/commands/blackbox-rm.ts +57 -0
- package/src/commands/blackbox.ts +48 -0
- package/src/commands/break-ls.ts +57 -0
- package/src/commands/break-rm.ts +40 -0
- package/src/commands/break-toggle.ts +42 -0
- package/src/commands/break.ts +145 -0
- package/src/commands/breakable.ts +69 -0
- package/src/commands/catch.ts +38 -0
- package/src/commands/console.ts +61 -0
- package/src/commands/continue.ts +46 -0
- package/src/commands/eval.ts +70 -0
- package/src/commands/exceptions.ts +61 -0
- package/src/commands/hotpatch.ts +67 -0
- package/src/commands/launch.ts +69 -0
- package/src/commands/logpoint.ts +78 -0
- package/src/commands/pause.ts +46 -0
- package/src/commands/props.ts +77 -0
- package/src/commands/restart-frame.ts +36 -0
- package/src/commands/run-to.ts +70 -0
- package/src/commands/scripts.ts +57 -0
- package/src/commands/search.ts +73 -0
- package/src/commands/sessions.ts +71 -0
- package/src/commands/set-return.ts +49 -0
- package/src/commands/set.ts +61 -0
- package/src/commands/source.ts +59 -0
- package/src/commands/sourcemap.ts +66 -0
- package/src/commands/stack.ts +64 -0
- package/src/commands/state.ts +124 -0
- package/src/commands/status.ts +57 -0
- package/src/commands/step.ts +50 -0
- package/src/commands/stop.ts +27 -0
- package/src/commands/vars.ts +71 -0
- package/src/daemon/client.ts +147 -0
- package/src/daemon/entry.ts +242 -0
- package/src/daemon/paths.ts +26 -0
- package/src/daemon/server.ts +185 -0
- package/src/daemon/session-blackbox.ts +41 -0
- package/src/daemon/session-breakpoints.ts +492 -0
- package/src/daemon/session-execution.ts +121 -0
- package/src/daemon/session-inspection.ts +701 -0
- package/src/daemon/session-mutation.ts +197 -0
- package/src/daemon/session-state.ts +258 -0
- package/src/daemon/session.ts +938 -0
- package/src/daemon/spawn.ts +53 -0
- package/src/formatter/errors.ts +15 -0
- package/src/formatter/source.ts +74 -0
- package/src/formatter/stack.ts +70 -0
- package/src/formatter/values.ts +269 -0
- package/src/formatter/variables.ts +20 -0
- package/src/main.ts +45 -0
- package/src/protocol/messages.ts +316 -0
- package/src/refs/ref-table.ts +120 -0
- package/src/refs/resolver.ts +24 -0
- package/src/sourcemap/resolver.ts +318 -0
- package/tests/fixtures/async-app.js +34 -0
- package/tests/fixtures/console-app.js +12 -0
- package/tests/fixtures/error-app.js +28 -0
- package/tests/fixtures/exception-app.js +6 -0
- package/tests/fixtures/inspect-app.js +10 -0
- package/tests/fixtures/mutation-app.js +9 -0
- package/tests/fixtures/simple-app.js +50 -0
- package/tests/fixtures/step-app.js +13 -0
- package/tests/fixtures/ts-app/src/app.ts +21 -0
- package/tests/fixtures/ts-app/tsconfig.json +14 -0
- package/tests/integration/blackbox.test.ts +135 -0
- package/tests/integration/break-extras.test.ts +241 -0
- package/tests/integration/breakpoint.test.ts +217 -0
- package/tests/integration/console.test.ts +275 -0
- package/tests/integration/execution.test.ts +247 -0
- package/tests/integration/inspection.test.ts +311 -0
- package/tests/integration/mutation.test.ts +178 -0
- package/tests/integration/session.test.ts +223 -0
- package/tests/integration/source.test.ts +209 -0
- package/tests/integration/sourcemap.test.ts +214 -0
- package/tests/integration/state.test.ts +208 -0
- package/tests/unit/cdp-client.test.ts +422 -0
- package/tests/unit/daemon.test.ts +286 -0
- package/tests/unit/formatter.test.ts +716 -0
- package/tests/unit/parser.test.ts +105 -0
- package/tests/unit/refs.test.ts +383 -0
- package/tests/unit/sourcemap.test.ts +236 -0
- package/tsconfig.json +32 -0
package/ndbg-spec.md
ADDED
|
@@ -0,0 +1,958 @@
|
|
|
1
|
+
# ndbg — Node.js Debugger CLI for AI Agents
|
|
2
|
+
|
|
3
|
+
## Specification v1.0
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Vision
|
|
8
|
+
|
|
9
|
+
`ndbg` is a command-line debugger for Node.js designed specifically for AI coding agents (Claude Code, Codex, Cursor, Gemini CLI, etc.). It wraps the V8 Inspector Protocol (Chrome DevTools Protocol) behind a token-efficient, stateless CLI interface that lets agents autonomously set breakpoints, step through code, inspect variables, profile memory, and even hot-patch code — all through bash.
|
|
10
|
+
|
|
11
|
+
**Core principle:** every tool call should return maximum debugging insight for minimum token cost.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. Design Philosophy
|
|
16
|
+
|
|
17
|
+
### 2.1 Agent-first, human-usable
|
|
18
|
+
|
|
19
|
+
The primary consumer is an LLM agent operating via bash tool calls. Every design decision optimizes for:
|
|
20
|
+
|
|
21
|
+
- **Token efficiency** — compact output, no ANSI colors by default, no decorative chrome
|
|
22
|
+
- **Minimum round-trips** — composite commands, auto-state-return after execution commands
|
|
23
|
+
- **Referenceability** — `@ref` system so agents never type long identifiers
|
|
24
|
+
- **Progressive verbosity** — control output granularity per-call with flags
|
|
25
|
+
- **Actionable errors** — every error suggests the next valid command
|
|
26
|
+
|
|
27
|
+
Humans can use it too. `--color` flag enables ANSI colors for terminal use.
|
|
28
|
+
|
|
29
|
+
### 2.2 CLI over MCP
|
|
30
|
+
|
|
31
|
+
ndbg is a CLI tool, not an MCP server. Rationale:
|
|
32
|
+
|
|
33
|
+
- **Zero setup** — `npm i -g ndbg` or `npx ndbg`, no MCP config files
|
|
34
|
+
- **Context efficient** — no JSON-RPC overhead, no schema in every call
|
|
35
|
+
- **Composable** — can pipe to grep/jq, chain with other bash commands
|
|
36
|
+
- **Universal** — works with any agent that has shell access, not just MCP clients
|
|
37
|
+
- **Reliable** — no persistent server process to crash or manage
|
|
38
|
+
|
|
39
|
+
### 2.3 Daemon architecture
|
|
40
|
+
|
|
41
|
+
Debugging is inherently stateful (a process paused at a breakpoint). ndbg uses a background daemon model:
|
|
42
|
+
|
|
43
|
+
- `ndbg launch` starts a daemon that holds the WebSocket connection to the V8 inspector
|
|
44
|
+
- All subsequent CLI calls communicate with the daemon via a local Unix socket
|
|
45
|
+
- Each CLI invocation is fast and stateless from the agent's perspective
|
|
46
|
+
- The daemon manages the debug session lifecycle
|
|
47
|
+
- Daemon auto-terminates when the debugged process exits or after an idle timeout
|
|
48
|
+
|
|
49
|
+
### 2.4 Built with Bun
|
|
50
|
+
|
|
51
|
+
- Written in TypeScript, compiled with `bun build --compile` to a single standalone binary
|
|
52
|
+
- Native WebSocket support for CDP communication
|
|
53
|
+
- Fast startup (~5ms for compiled binary)
|
|
54
|
+
- No runtime dependency — users don't need Bun or Node installed to use the compiled binary
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 3. The @ref System
|
|
59
|
+
|
|
60
|
+
Inspired by agent-browser's `@e1` element refs, ndbg assigns short stable references to every inspectable entity in its output. Agents use these refs instead of long object IDs, file paths, or frame indices.
|
|
61
|
+
|
|
62
|
+
### 3.1 Ref types
|
|
63
|
+
|
|
64
|
+
| Prefix | Entity | Example | Lifetime |
|
|
65
|
+
|--------|--------|---------|----------|
|
|
66
|
+
| `@v` | Variable / value | `@v1`, `@v2` | Until next pause (step/continue) |
|
|
67
|
+
| `@f` | Stack frame | `@f0`, `@f1` | Until next pause |
|
|
68
|
+
| `@o` | Expanded object | `@o1`, `@o2` | Until session ends or `ndbg gc-refs` |
|
|
69
|
+
| `BP#` | Breakpoint | `BP#1`, `BP#2` | Until removed |
|
|
70
|
+
| `LP#` | Logpoint | `LP#1`, `LP#2` | Until removed |
|
|
71
|
+
| `HS#` | Heap snapshot | `HS#1`, `HS#2` | Until session ends |
|
|
72
|
+
|
|
73
|
+
### 3.2 Ref usage
|
|
74
|
+
|
|
75
|
+
Refs can be used anywhere an identifier is expected:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
ndbg props @v1 # expand variable @v1
|
|
79
|
+
ndbg eval "@v1.retryCount" # use ref in expressions
|
|
80
|
+
ndbg set @v2 true # mutate variable @v2
|
|
81
|
+
ndbg frame @f1 # switch to stack frame @f1
|
|
82
|
+
ndbg restart-frame @f0 # restart frame @f0
|
|
83
|
+
ndbg break-rm BP#1 # remove breakpoint BP#1
|
|
84
|
+
ndbg heap diff HS#1 HS#2 # diff two heap snapshots
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 3.3 Ref resolution
|
|
88
|
+
|
|
89
|
+
The daemon maintains a ref table mapping short refs to V8 RemoteObjectIds, CallFrameIds, BreakpointIds, etc. The table is:
|
|
90
|
+
|
|
91
|
+
- **Regenerated** on each pause event (for `@v` and `@f` refs)
|
|
92
|
+
- **Append-only** for `@o` refs (expanded objects persist across pauses)
|
|
93
|
+
- **Stable** for `BP#`, `LP#`, `HS#` refs (persist until explicitly removed)
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 4. The State Snapshot
|
|
98
|
+
|
|
99
|
+
The `state` command is the primary "where am I?" command. It returns a structured view of the current debug context, analogous to agent-browser's `snapshot`.
|
|
100
|
+
|
|
101
|
+
### 4.1 Default output
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
⏸ Paused at src/queue/processor.ts:47 (BP#1, hit #3) [resumed 1.2s ago]
|
|
105
|
+
|
|
106
|
+
Source:
|
|
107
|
+
45│ async processJob(job: Job) {
|
|
108
|
+
46│ const lock = await this.acquireLock(job.id);
|
|
109
|
+
→ 47│ if (!lock) return;
|
|
110
|
+
48│ const result = await this.execute(job);
|
|
111
|
+
49│ await this.markComplete(job.id);
|
|
112
|
+
|
|
113
|
+
Locals:
|
|
114
|
+
@v1 job Job { id: "test-123", type: "email", retries: 2 }
|
|
115
|
+
@v2 lock false
|
|
116
|
+
@v3 this QueueProcessor { workerId: "worker-a", redis: [Redis] }
|
|
117
|
+
|
|
118
|
+
Stack:
|
|
119
|
+
@f0 processJob src/queue/processor.ts:47
|
|
120
|
+
@f1 poll src/queue/processor.ts:71
|
|
121
|
+
@f2 setTimeout cb node:internal/timers:573
|
|
122
|
+
┊ async
|
|
123
|
+
@f3 QueueProcessor.start src/queue/processor.ts:12
|
|
124
|
+
|
|
125
|
+
Breakpoints:
|
|
126
|
+
BP#1 src/queue/processor.ts:47 (cond: job.id === 'test-123') hits: 3
|
|
127
|
+
BP#2 src/queue/processor.ts:31
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 4.2 Filtering flags
|
|
131
|
+
|
|
132
|
+
| Flag | Output |
|
|
133
|
+
|------|--------|
|
|
134
|
+
| (none) | Full state: source + locals + stack + breakpoints |
|
|
135
|
+
| `-v` / `--vars` | Locals only |
|
|
136
|
+
| `-s` / `--stack` | Stack trace only |
|
|
137
|
+
| `-b` / `--breakpoints` | Breakpoints/logpoints only |
|
|
138
|
+
| `-c` / `--code` | Source context only |
|
|
139
|
+
| `--compact` | One-line-per-section summary |
|
|
140
|
+
| `--depth N` | Object expansion depth (default: 1) |
|
|
141
|
+
| `--lines N` | Source context lines above/below (default: 3) |
|
|
142
|
+
| `--frame @fN` | Show state from perspective of frame N |
|
|
143
|
+
| `--all-scopes` | Include closure and global scope, not just locals |
|
|
144
|
+
| `--json` | Full state as JSON (for programmatic use) |
|
|
145
|
+
|
|
146
|
+
### 4.3 Auto-state return
|
|
147
|
+
|
|
148
|
+
**Every execution command returns a state snapshot automatically.** This is a critical design choice that halves the number of tool calls an agent needs.
|
|
149
|
+
|
|
150
|
+
Commands that return state: `continue`, `step`, `step over`, `step into`, `step out`, `run-to`, `restart-frame`, `pause`.
|
|
151
|
+
|
|
152
|
+
The auto-returned state uses the same format as `ndbg state` and respects a configurable default verbosity (see `ndbg config`).
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 5. Command Reference
|
|
157
|
+
|
|
158
|
+
### 5.1 Session Management
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
ndbg launch [--brk] [--session NAME] <command...>
|
|
162
|
+
```
|
|
163
|
+
Start a Node.js process with `--inspect` (or `--inspect-brk` if `--brk` is passed) and attach the debugger daemon. Returns initial state if `--brk`.
|
|
164
|
+
|
|
165
|
+
- `--brk` — pause on first line of user code (recommended for most debugging)
|
|
166
|
+
- `--session NAME` — assign a name for multi-session debugging
|
|
167
|
+
- `--port PORT` — use specific inspector port (default: auto)
|
|
168
|
+
- `--timeout SECS` — daemon idle timeout (default: 300)
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
ndbg attach <pid | ws-url | port>
|
|
172
|
+
```
|
|
173
|
+
Attach to an already-running Node.js process (started with `--inspect`).
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
ndbg stop [--session NAME]
|
|
177
|
+
```
|
|
178
|
+
Kill the debugged process and shut down the daemon.
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
ndbg sessions
|
|
182
|
+
```
|
|
183
|
+
List active debug sessions with PID, status (paused/running), and session name.
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
ndbg sessions --cleanup
|
|
187
|
+
```
|
|
188
|
+
Kill all orphaned daemon processes.
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
ndbg status
|
|
192
|
+
```
|
|
193
|
+
Current session info: PID, pause state, attached breakpoints, memory usage, uptime.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### 5.2 Breakpoints
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
ndbg break <file>:<line> [OPTIONS]
|
|
201
|
+
```
|
|
202
|
+
Set a breakpoint. Returns the breakpoint ID and resolved location (with source map).
|
|
203
|
+
|
|
204
|
+
Options:
|
|
205
|
+
- `--condition <expr>` — only pause when expression is truthy
|
|
206
|
+
- `--hit-count <n>` — only pause on the Nth hit
|
|
207
|
+
- `--continue` — immediately continue after setting (composite command)
|
|
208
|
+
- `--log <template>` — convert to logpoint (shortcut for `ndbg logpoint`)
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
ndbg break --pattern <urlRegex>:<line>
|
|
212
|
+
```
|
|
213
|
+
Set breakpoint on all files matching a URL regex pattern. Useful for breaking in `node_modules` or dynamically loaded scripts.
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
ndbg break-fn <expr>
|
|
217
|
+
```
|
|
218
|
+
Set a breakpoint on every call to the function returned by evaluating `<expr>`. Example: `ndbg break-fn "require('express').Router"`.
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
ndbg break-on-load [--sourcemap]
|
|
222
|
+
```
|
|
223
|
+
Break whenever a new script is parsed. With `--sourcemap`, only break on scripts with source maps (i.e., your code, not node internals).
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
ndbg break-rm <BP#id | LP#id | all>
|
|
227
|
+
```
|
|
228
|
+
Remove a breakpoint or logpoint by ref. `all` removes everything.
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
ndbg break-ls
|
|
232
|
+
```
|
|
233
|
+
List all breakpoints and logpoints with their locations, conditions, and hit counts.
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
ndbg break-toggle [BP#id | all]
|
|
237
|
+
```
|
|
238
|
+
Enable/disable breakpoint(s) without removing them.
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
ndbg breakable <file>:<start>-<end>
|
|
242
|
+
```
|
|
243
|
+
List valid breakpoint locations in a line range. Useful when the agent picks a non-breakable line.
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
ndbg logpoint <file>:<line> <template>
|
|
247
|
+
```
|
|
248
|
+
Set a logpoint — logs the interpolated template string each time the line is hit, without pausing. Template uses `${expr}` syntax.
|
|
249
|
+
|
|
250
|
+
- `--max <n>` — auto-pause after N log emissions (default: 100, prevents floods)
|
|
251
|
+
- `--condition <expr>` — only log when condition is true
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
ndbg catch [all | uncaught | caught | none]
|
|
255
|
+
```
|
|
256
|
+
Configure pause-on-exception behavior. `all` catches even exceptions inside try/catch. `uncaught` is the most useful default.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
### 5.3 Execution Control
|
|
261
|
+
|
|
262
|
+
All execution commands **return a state snapshot** when the process next pauses.
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
ndbg continue
|
|
266
|
+
```
|
|
267
|
+
Resume execution until next breakpoint, exception, or manual pause.
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
ndbg step [over | into | out]
|
|
271
|
+
```
|
|
272
|
+
Step one statement. Default is `over`.
|
|
273
|
+
|
|
274
|
+
- `step into` — with `--break-on-async` flag, pauses on the first async task scheduled before the next pause
|
|
275
|
+
- `step over` / `step into` — accept `--skip <pattern>` to skip over matching files (inline blackboxing)
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
ndbg run-to <file>:<line>
|
|
279
|
+
```
|
|
280
|
+
Continue execution until a specific location. Does not create a persistent breakpoint.
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
ndbg restart-frame [@fN]
|
|
284
|
+
```
|
|
285
|
+
Re-execute the specified frame (or current frame) from the beginning. The process continues immediately and pauses at the beginning of the restarted function.
|
|
286
|
+
|
|
287
|
+
```
|
|
288
|
+
ndbg pause
|
|
289
|
+
```
|
|
290
|
+
Interrupt a running process. Returns state at the interrupt point.
|
|
291
|
+
|
|
292
|
+
```
|
|
293
|
+
ndbg kill-execution
|
|
294
|
+
```
|
|
295
|
+
Terminate the current JavaScript execution without killing the Node.js process. Useful for stopping infinite loops while keeping the debug session alive.
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
### 5.4 Inspection
|
|
300
|
+
|
|
301
|
+
```
|
|
302
|
+
ndbg state [FLAGS]
|
|
303
|
+
```
|
|
304
|
+
Return the current debug state snapshot. See Section 4 for flags.
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
ndbg vars [name1, name2, ...]
|
|
308
|
+
```
|
|
309
|
+
Show local variables in the current frame. Optionally filter to specific names. Output assigns `@v` refs.
|
|
310
|
+
|
|
311
|
+
```
|
|
312
|
+
ndbg stack [--async-depth N]
|
|
313
|
+
```
|
|
314
|
+
Show the call stack. `--async-depth` controls how many async frames to resolve (default: 8, 0 to disable).
|
|
315
|
+
|
|
316
|
+
```
|
|
317
|
+
ndbg eval <expression>
|
|
318
|
+
```
|
|
319
|
+
Evaluate an expression in the context of the current call frame.
|
|
320
|
+
|
|
321
|
+
- Supports `await` (uses CDP's `awaitPromise`)
|
|
322
|
+
- Supports `@ref` interpolation: `ndbg eval "@v1.retryCount"`
|
|
323
|
+
- `--frame @fN` — evaluate in a specific frame
|
|
324
|
+
- `--silent` — suppress exception reporting
|
|
325
|
+
- `--timeout MS` — kill evaluation after N milliseconds (default: 5000)
|
|
326
|
+
- `--side-effect-free` — throw if expression has side effects (safe inspection)
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
ndbg props <@ref> [OPTIONS]
|
|
330
|
+
```
|
|
331
|
+
Expand the properties of an object ref. Returns `@o` refs for nested values.
|
|
332
|
+
|
|
333
|
+
- `--own` — only own properties (skip prototype chain)
|
|
334
|
+
- `--depth N` — recursive expansion depth (default: 1)
|
|
335
|
+
- `--private` — include private fields
|
|
336
|
+
- `--internal` — include V8 internal properties (e.g., `[[PromiseState]]`)
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
ndbg instances <expression>
|
|
340
|
+
```
|
|
341
|
+
Find all live instances of a prototype/constructor. Evaluates the expression to get a prototype, then queries all objects sharing it. Example: `ndbg instances "EventEmitter.prototype"`.
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
ndbg globals
|
|
345
|
+
```
|
|
346
|
+
List all global `let`, `const`, and `class` declarations in the current execution context.
|
|
347
|
+
|
|
348
|
+
```
|
|
349
|
+
ndbg source [OPTIONS]
|
|
350
|
+
```
|
|
351
|
+
Show source code around the current pause location.
|
|
352
|
+
|
|
353
|
+
- `--lines N` — lines above and below (default: 5)
|
|
354
|
+
- `--file <path>` — show source of a different file
|
|
355
|
+
- `--all` — show full file
|
|
356
|
+
|
|
357
|
+
```
|
|
358
|
+
ndbg search <query> [OPTIONS]
|
|
359
|
+
```
|
|
360
|
+
Search across all loaded scripts.
|
|
361
|
+
|
|
362
|
+
- `--regex` — treat query as regex
|
|
363
|
+
- `--case-sensitive` — case-sensitive search (default: insensitive)
|
|
364
|
+
- `--file <scriptId>` — search within a specific script
|
|
365
|
+
|
|
366
|
+
```
|
|
367
|
+
ndbg scripts [--filter <pattern>]
|
|
368
|
+
```
|
|
369
|
+
List all loaded scripts (files). Useful to find the right `scriptId` for breakpoints in dynamically loaded code.
|
|
370
|
+
|
|
371
|
+
```
|
|
372
|
+
ndbg console [OPTIONS]
|
|
373
|
+
```
|
|
374
|
+
Show captured console output (log, warn, error, etc.) with timestamps and stack traces.
|
|
375
|
+
|
|
376
|
+
- `--follow` — stream output in real-time (blocks until Ctrl+C or `--max`)
|
|
377
|
+
- `--since <N>` — only show last N entries
|
|
378
|
+
- `--level <log|warn|error>` — filter by level
|
|
379
|
+
- `--clear` — clear captured console buffer
|
|
380
|
+
|
|
381
|
+
```
|
|
382
|
+
ndbg exceptions [OPTIONS]
|
|
383
|
+
```
|
|
384
|
+
Show captured uncaught exceptions.
|
|
385
|
+
|
|
386
|
+
- `--follow` — stream in real-time
|
|
387
|
+
- `--since <N>` — last N entries
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
### 5.5 Mutation
|
|
392
|
+
|
|
393
|
+
These commands let an agent **test hypotheses and fixes without restarting**.
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
ndbg set <@vRef | varName> <value>
|
|
397
|
+
```
|
|
398
|
+
Change the value of a local, closure, or catch-scope variable in the current frame.
|
|
399
|
+
|
|
400
|
+
- Value is parsed as JSON or as a JavaScript primitive
|
|
401
|
+
- Only works on `local`, `closure`, and `catch` scope types (V8 limitation)
|
|
402
|
+
- Example: `ndbg set @v2 true`, `ndbg set retryCount 0`
|
|
403
|
+
|
|
404
|
+
```
|
|
405
|
+
ndbg set-return <value>
|
|
406
|
+
```
|
|
407
|
+
Change the return value of the current function. Only available when paused at a return point.
|
|
408
|
+
|
|
409
|
+
```
|
|
410
|
+
ndbg hotpatch <file>
|
|
411
|
+
```
|
|
412
|
+
Live-edit the source of a loaded script. Reads the file from disk and pushes it to V8 via `Debugger.setScriptSource`.
|
|
413
|
+
|
|
414
|
+
- If the edited function is the top-most stack frame (and only activation), it auto-restarts
|
|
415
|
+
- `--dry-run` — check if the edit would succeed without applying
|
|
416
|
+
- Returns status: `Ok`, `CompileError`, `BlockedByActiveFunction`, etc.
|
|
417
|
+
- **This is ndbg's killer feature for agents** — fix code and immediately verify, no restart
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
### 5.6 Blackboxing
|
|
422
|
+
|
|
423
|
+
Control which code the debugger steps into, preventing agents from getting lost in framework internals.
|
|
424
|
+
|
|
425
|
+
```
|
|
426
|
+
ndbg blackbox <pattern...>
|
|
427
|
+
```
|
|
428
|
+
Skip stepping into scripts matching the given patterns (regex). Stepping into a blackboxed function will step over it instead.
|
|
429
|
+
|
|
430
|
+
- Example: `ndbg blackbox "node_modules" "internal/"`
|
|
431
|
+
- Stacks with previous patterns
|
|
432
|
+
|
|
433
|
+
```
|
|
434
|
+
ndbg blackbox-ls
|
|
435
|
+
```
|
|
436
|
+
List current blackbox patterns.
|
|
437
|
+
|
|
438
|
+
```
|
|
439
|
+
ndbg blackbox-rm <pattern | all>
|
|
440
|
+
```
|
|
441
|
+
Remove blackbox patterns.
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
### 5.7 CPU Profiling
|
|
446
|
+
|
|
447
|
+
```
|
|
448
|
+
ndbg cpu start [--interval <μs>]
|
|
449
|
+
```
|
|
450
|
+
Start the V8 CPU profiler. Default sampling interval is 1000μs.
|
|
451
|
+
|
|
452
|
+
```
|
|
453
|
+
ndbg cpu stop [--top N]
|
|
454
|
+
```
|
|
455
|
+
Stop profiling and return results.
|
|
456
|
+
|
|
457
|
+
- `--top N` — show top N hottest functions (default: 10)
|
|
458
|
+
- Output includes: function name, file:line, self time %, total time %, deopt reason (if any)
|
|
459
|
+
- Full profile saved to a file for external tools
|
|
460
|
+
|
|
461
|
+
```
|
|
462
|
+
ndbg coverage start [--detailed]
|
|
463
|
+
```
|
|
464
|
+
Start precise code coverage collection. `--detailed` enables block-level granularity (not just function-level).
|
|
465
|
+
|
|
466
|
+
```
|
|
467
|
+
ndbg coverage stop [OPTIONS]
|
|
468
|
+
```
|
|
469
|
+
Stop coverage and report.
|
|
470
|
+
|
|
471
|
+
- `--file <path>` — filter to a specific file
|
|
472
|
+
- `--uncovered` — only show uncovered lines/blocks
|
|
473
|
+
- Output: per-function execution counts and uncovered ranges
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
### 5.8 Memory / Heap
|
|
478
|
+
|
|
479
|
+
```
|
|
480
|
+
ndbg heap usage
|
|
481
|
+
```
|
|
482
|
+
Quick heap statistics: used, total, embedder heap, backing store. No snapshot needed.
|
|
483
|
+
|
|
484
|
+
```
|
|
485
|
+
ndbg heap snapshot [--tag <name>]
|
|
486
|
+
```
|
|
487
|
+
Take a full V8 heap snapshot. Assigns an `HS#` ref.
|
|
488
|
+
|
|
489
|
+
```
|
|
490
|
+
ndbg heap diff <HS#a> <HS#b> [--top N]
|
|
491
|
+
```
|
|
492
|
+
Compare two heap snapshots. Output: table of constructors with delta count and delta size, sorted by size impact.
|
|
493
|
+
|
|
494
|
+
```
|
|
495
|
+
ndbg heap sample start [--interval <bytes>] [--include-gc]
|
|
496
|
+
```
|
|
497
|
+
Start sampling heap profiler. Lightweight alternative to full snapshots.
|
|
498
|
+
|
|
499
|
+
- `--interval` — average sampling interval in bytes (default: 32768)
|
|
500
|
+
- `--include-gc` — include objects already garbage-collected (shows temporary allocations)
|
|
501
|
+
|
|
502
|
+
```
|
|
503
|
+
ndbg heap sample stop [--top N]
|
|
504
|
+
```
|
|
505
|
+
Stop sampling and report top allocation sites.
|
|
506
|
+
|
|
507
|
+
```
|
|
508
|
+
ndbg heap track start
|
|
509
|
+
```
|
|
510
|
+
Start allocation tracking over time (timeline mode).
|
|
511
|
+
|
|
512
|
+
```
|
|
513
|
+
ndbg heap track stop
|
|
514
|
+
```
|
|
515
|
+
Stop tracking and report allocation rate by callsite.
|
|
516
|
+
|
|
517
|
+
```
|
|
518
|
+
ndbg heap inspect <heapObjectId>
|
|
519
|
+
```
|
|
520
|
+
Get a remote object reference from a heap snapshot node, bridging heap analysis with runtime inspection. Returns an `@o` ref.
|
|
521
|
+
|
|
522
|
+
```
|
|
523
|
+
ndbg gc
|
|
524
|
+
```
|
|
525
|
+
Force a garbage collection cycle. Useful before taking a snapshot to see what truly leaks.
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
### 5.9 Advanced / Utility
|
|
530
|
+
|
|
531
|
+
```
|
|
532
|
+
ndbg inject-hook <name>
|
|
533
|
+
```
|
|
534
|
+
Create a runtime binding that, when called from application code, sends a notification to the daemon. Use for custom instrumentation.
|
|
535
|
+
|
|
536
|
+
- Adds a global function `__ndbg_<name>()` that the app can call
|
|
537
|
+
- When called, the daemon captures the call's arguments and stack
|
|
538
|
+
- View with: `ndbg hooks [--follow]`
|
|
539
|
+
|
|
540
|
+
```
|
|
541
|
+
ndbg contexts
|
|
542
|
+
```
|
|
543
|
+
List all V8 execution contexts (useful for debugging Jest VM sandboxes, workers, or vm.runInContext scenarios). Each context gets an ID.
|
|
544
|
+
|
|
545
|
+
```
|
|
546
|
+
ndbg async-depth <N>
|
|
547
|
+
```
|
|
548
|
+
Set the async call stack trace depth. Default: 16. Set to 0 to disable async stacks. Higher values cost more memory but show the full async chain.
|
|
549
|
+
|
|
550
|
+
```
|
|
551
|
+
ndbg config [key] [value]
|
|
552
|
+
```
|
|
553
|
+
Get/set daemon configuration:
|
|
554
|
+
|
|
555
|
+
- `auto-state` — verbosity of auto-returned state snapshots (default: `full`)
|
|
556
|
+
- `default-depth` — default object expansion depth (default: 1)
|
|
557
|
+
- `default-lines` — default source context lines (default: 3)
|
|
558
|
+
- `async-depth` — async stack trace depth (default: 16)
|
|
559
|
+
- `blackbox` — default blackbox patterns
|
|
560
|
+
- `timeout` — daemon idle timeout in seconds
|
|
561
|
+
|
|
562
|
+
```
|
|
563
|
+
ndbg gc-refs
|
|
564
|
+
```
|
|
565
|
+
Clear accumulated `@o` refs to free memory. `@v` and `@f` refs are cleared automatically on each pause.
|
|
566
|
+
|
|
567
|
+
```
|
|
568
|
+
ndbg --help-agent
|
|
569
|
+
```
|
|
570
|
+
Output a compact LLM-optimized reference card with core workflow, quick reference, and common debugging patterns. Designed to be injected into an agent's context window.
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
## 6. Output Format
|
|
575
|
+
|
|
576
|
+
### 6.1 Principles
|
|
577
|
+
|
|
578
|
+
1. **Plain text by default** — no ANSI escape codes, no box drawing (unless `--color`)
|
|
579
|
+
2. **Compact** — one entity per line where possible, tree indentation for hierarchy
|
|
580
|
+
3. **@refs inline** — every inspectable value is prefixed with its ref
|
|
581
|
+
4. **Timing annotations** — execution commands report elapsed time since last pause
|
|
582
|
+
5. **No JSON by default** — JSON wastes tokens on syntax. Available with `--json` flag
|
|
583
|
+
6. **Truncation with hints** — large outputs are truncated with a `... (ndbg props @oN for more)` hint
|
|
584
|
+
|
|
585
|
+
### 6.2 Variable formatting
|
|
586
|
+
|
|
587
|
+
Variables are displayed with smart truncation:
|
|
588
|
+
|
|
589
|
+
```
|
|
590
|
+
@v1 job Job { id: "test-123", type: "email", retries: 2, payload: {...} }
|
|
591
|
+
@v2 lock false
|
|
592
|
+
@v3 items Array(47) [ "a", "b", "c", ... ]
|
|
593
|
+
@v4 callback Function processResult(job)
|
|
594
|
+
@v5 promise Promise { <pending> }
|
|
595
|
+
@v6 error Error: "connection refused" (at src/db.ts:12)
|
|
596
|
+
@v7 map Map(3) { "a" => 1, "b" => 2, "c" => 3 }
|
|
597
|
+
@v8 buf Buffer(1024) <48 65 6c 6c 6f ...>
|
|
598
|
+
@v9 undefined undefined
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
- Objects: constructor name + top-level properties up to ~80 chars, then `{...}`
|
|
602
|
+
- Arrays: type + length + first 3 elements, then `...`
|
|
603
|
+
- Functions: `Function` + name + parameters
|
|
604
|
+
- Promises: state (`<pending>`, `<resolved: value>`, `<rejected: error>`)
|
|
605
|
+
- Errors: message + first stack frame
|
|
606
|
+
- Buffers: length + first 5 hex bytes
|
|
607
|
+
|
|
608
|
+
### 6.3 Source code formatting
|
|
609
|
+
|
|
610
|
+
```
|
|
611
|
+
45│ async processJob(job: Job) {
|
|
612
|
+
46│ const lock = await this.acquireLock(job.id);
|
|
613
|
+
→ 47│ if (!lock) return;
|
|
614
|
+
48│ const result = await this.execute(job);
|
|
615
|
+
49│ await this.markComplete(job.id);
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
- Line numbers right-aligned, pipe separator
|
|
619
|
+
- Arrow `→` at current execution line
|
|
620
|
+
- Source-mapped locations (show `.ts` not `.js`)
|
|
621
|
+
- Breakpoint markers: `●` at lines with breakpoints
|
|
622
|
+
|
|
623
|
+
### 6.4 Stack trace formatting
|
|
624
|
+
|
|
625
|
+
```
|
|
626
|
+
@f0 processJob src/queue/processor.ts:47
|
|
627
|
+
@f1 poll src/queue/processor.ts:71
|
|
628
|
+
@f2 setTimeout cb node:internal/timers:573
|
|
629
|
+
┊ async gap
|
|
630
|
+
@f3 QueueProcessor.start src/queue/processor.ts:12
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
- Frame ref, function name, source-mapped location
|
|
634
|
+
- `┊ async gap` markers between async boundaries
|
|
635
|
+
- Blackboxed frames shown dimmed or collapsed: `┊ ... 3 framework frames (blackboxed)`
|
|
636
|
+
|
|
637
|
+
### 6.5 Error output
|
|
638
|
+
|
|
639
|
+
Errors always suggest the next action:
|
|
640
|
+
|
|
641
|
+
```
|
|
642
|
+
✗ Cannot set breakpoint at src/queue/processor.ts:46 — no breakable location
|
|
643
|
+
Nearest valid lines: 45, 47
|
|
644
|
+
→ Try: ndbg break src/queue/processor.ts:47
|
|
645
|
+
|
|
646
|
+
✗ Variable 'foo' not found in current scope
|
|
647
|
+
Available locals: job, lock, this
|
|
648
|
+
→ Try: ndbg vars
|
|
649
|
+
|
|
650
|
+
✗ Cannot step — process is running (not paused)
|
|
651
|
+
→ Try: ndbg pause
|
|
652
|
+
|
|
653
|
+
✗ Session "default" not found — no active debug session
|
|
654
|
+
→ Try: ndbg launch --brk "node app.js"
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
---
|
|
658
|
+
|
|
659
|
+
## 7. Source Map Support
|
|
660
|
+
|
|
661
|
+
Source maps are a first-class concern, not an afterthought.
|
|
662
|
+
|
|
663
|
+
### 7.1 Behavior
|
|
664
|
+
|
|
665
|
+
- All user-facing locations display **source-mapped paths and line numbers** (TypeScript, etc.)
|
|
666
|
+
- The `Debugger.scriptParsed` event provides `sourceMapURL`; the daemon fetches and caches it
|
|
667
|
+
- `ndbg break src/foo.ts:42` resolves to the generated `.js` location automatically
|
|
668
|
+
- Stack traces always show source-mapped locations
|
|
669
|
+
- `ndbg source` shows the original source (TypeScript), not compiled JS
|
|
670
|
+
- If no source map exists, the generated JS is shown (no error)
|
|
671
|
+
|
|
672
|
+
### 7.2 Source map commands
|
|
673
|
+
|
|
674
|
+
```
|
|
675
|
+
ndbg sourcemap <file> # Show source map info for a file
|
|
676
|
+
ndbg sourcemap --disable # Disable source map resolution globally
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
---
|
|
680
|
+
|
|
681
|
+
## 8. Session & Daemon Protocol
|
|
682
|
+
|
|
683
|
+
### 8.1 Daemon lifecycle
|
|
684
|
+
|
|
685
|
+
1. `ndbg launch` starts a background daemon process
|
|
686
|
+
2. Daemon opens a WebSocket to the Node.js inspector (`ws://127.0.0.1:<port>`)
|
|
687
|
+
3. Daemon listens on a Unix socket at `$XDG_RUNTIME_DIR/ndbg/<session-name>.sock` (or `$TMPDIR` fallback)
|
|
688
|
+
4. CLI commands connect to the Unix socket, send a request, receive a response, disconnect
|
|
689
|
+
5. Daemon shuts down when: process exits, `ndbg stop` is called, or idle timeout is reached
|
|
690
|
+
|
|
691
|
+
### 8.2 Internal protocol
|
|
692
|
+
|
|
693
|
+
CLI-to-daemon communication uses newline-delimited JSON over Unix socket. Each request/response is a single JSON object. This is internal — users never see it.
|
|
694
|
+
|
|
695
|
+
```json
|
|
696
|
+
// Request
|
|
697
|
+
{"cmd": "continue", "args": {}}
|
|
698
|
+
|
|
699
|
+
// Response
|
|
700
|
+
{"ok": true, "state": { ... }, "refs": { ... }}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### 8.3 Multi-session
|
|
704
|
+
|
|
705
|
+
- Each debug session has a unique name (default: `"default"`)
|
|
706
|
+
- Multiple sessions can run simultaneously (e.g., debugging a client and server)
|
|
707
|
+
- `--session NAME` on any command targets a specific session
|
|
708
|
+
- `ndbg sessions` lists all active sessions
|
|
709
|
+
|
|
710
|
+
### 8.4 Crash recovery
|
|
711
|
+
|
|
712
|
+
- If the daemon crashes, the CLI detects the dead socket and reports:
|
|
713
|
+
`✗ Session "default" daemon is not running. The debugged process (PID 42871) is still alive.`
|
|
714
|
+
`→ Try: ndbg attach 42871`
|
|
715
|
+
- Lock files prevent duplicate daemons for the same session
|
|
716
|
+
|
|
717
|
+
---
|
|
718
|
+
|
|
719
|
+
## 9. SKILL.md for Agent Integration
|
|
720
|
+
|
|
721
|
+
ndbg ships with a SKILL.md for Claude Code's skill system and compatible agents.
|
|
722
|
+
|
|
723
|
+
```yaml
|
|
724
|
+
---
|
|
725
|
+
name: node-debugger
|
|
726
|
+
description: >
|
|
727
|
+
Node.js runtime debugger CLI. Use when debugging runtime errors, race
|
|
728
|
+
conditions, memory leaks, circular dependencies, Jest/VM issues, or any
|
|
729
|
+
bug requiring breakpoints, variable inspection, stepping through code, or
|
|
730
|
+
profiling. Triggers: "debug", "breakpoint", "race condition", "memory leak",
|
|
731
|
+
"step through", "inspect at runtime", "profile", "heap snapshot", "why is
|
|
732
|
+
this variable undefined", "infinite loop".
|
|
733
|
+
---
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
### 9.1 `--help-agent` output
|
|
737
|
+
|
|
738
|
+
The `ndbg --help-agent` command outputs a compact reference designed for agent context windows:
|
|
739
|
+
|
|
740
|
+
```
|
|
741
|
+
ndbg — Node.js debugger CLI for AI agents
|
|
742
|
+
|
|
743
|
+
CORE LOOP:
|
|
744
|
+
1. ndbg launch --brk "node app.js" → pauses at first line, returns state
|
|
745
|
+
2. ndbg break src/file.ts:42 → set breakpoint
|
|
746
|
+
3. ndbg continue → run to breakpoint, returns state
|
|
747
|
+
4. Inspect: ndbg vars, ndbg eval, ndbg props @v1
|
|
748
|
+
5. Mutate/fix: ndbg set @v1 value, ndbg hotpatch src/file.ts
|
|
749
|
+
6. Repeat from 3
|
|
750
|
+
|
|
751
|
+
REFS: Every output assigns @refs. Use them everywhere:
|
|
752
|
+
@v1..@vN variables │ ndbg props @v1, ndbg set @v2 true
|
|
753
|
+
@f0..@fN stack frames │ ndbg frame @f1, ndbg restart-frame @f0
|
|
754
|
+
BP#1..N breakpoints │ ndbg break-rm BP#1
|
|
755
|
+
HS#1..N heap snaps │ ndbg heap diff HS#1 HS#2
|
|
756
|
+
|
|
757
|
+
EXECUTION (all return state automatically):
|
|
758
|
+
ndbg continue Resume to next breakpoint
|
|
759
|
+
ndbg step [over|into|out] Step one statement
|
|
760
|
+
ndbg run-to file:line Continue to location
|
|
761
|
+
ndbg pause Interrupt running process
|
|
762
|
+
ndbg restart-frame @f0 Re-run current function
|
|
763
|
+
|
|
764
|
+
BREAKPOINTS:
|
|
765
|
+
ndbg break file:line [--condition expr]
|
|
766
|
+
ndbg logpoint file:line "template ${var}"
|
|
767
|
+
ndbg catch [all|uncaught|none]
|
|
768
|
+
ndbg blackbox "node_modules/**"
|
|
769
|
+
|
|
770
|
+
INSPECTION:
|
|
771
|
+
ndbg state [-v|-s|-b|-c] [--depth N]
|
|
772
|
+
ndbg vars [name...]
|
|
773
|
+
ndbg eval <expr> (await supported)
|
|
774
|
+
ndbg props @ref [--depth N]
|
|
775
|
+
ndbg instances "Class.prototype"
|
|
776
|
+
ndbg search "query" [--regex]
|
|
777
|
+
|
|
778
|
+
MUTATION:
|
|
779
|
+
ndbg set @v1 <value> Change variable
|
|
780
|
+
ndbg set-return <value> Change return value
|
|
781
|
+
ndbg hotpatch <file> Live-edit code (no restart!)
|
|
782
|
+
|
|
783
|
+
PROFILING:
|
|
784
|
+
ndbg cpu start / stop [--top N]
|
|
785
|
+
ndbg coverage start [--detailed] / stop [--uncovered]
|
|
786
|
+
ndbg heap usage | snapshot | diff | sample | gc
|
|
787
|
+
|
|
788
|
+
PATTERNS:
|
|
789
|
+
# Race condition → trace with logpoints
|
|
790
|
+
ndbg logpoint src/lock.ts:31 "acquire: ${jobId} by ${workerId}"
|
|
791
|
+
ndbg continue
|
|
792
|
+
|
|
793
|
+
# Circular dependency → trace require chain
|
|
794
|
+
ndbg break-on-load --sourcemap
|
|
795
|
+
ndbg logpoint node_modules/jest-runtime/build/index.js:348 "${from} → ${moduleName}"
|
|
796
|
+
|
|
797
|
+
# Memory leak → snapshot before/after
|
|
798
|
+
ndbg heap snapshot --tag before
|
|
799
|
+
# ... trigger load ...
|
|
800
|
+
ndbg heap snapshot --tag after
|
|
801
|
+
ndbg heap diff HS#1 HS#2 --top 5
|
|
802
|
+
|
|
803
|
+
# Skip framework noise
|
|
804
|
+
ndbg blackbox "node_modules" "internal/"
|
|
805
|
+
```
|
|
806
|
+
|
|
807
|
+
---
|
|
808
|
+
|
|
809
|
+
## 10. Installation & Distribution
|
|
810
|
+
|
|
811
|
+
### 10.1 Install methods
|
|
812
|
+
|
|
813
|
+
```bash
|
|
814
|
+
# npx (zero install — recommended for first use)
|
|
815
|
+
npx ndbg launch --brk "node app.js"
|
|
816
|
+
|
|
817
|
+
# Global install
|
|
818
|
+
npm install -g ndbg
|
|
819
|
+
|
|
820
|
+
# Compiled binary (no runtime needed)
|
|
821
|
+
# Download from GitHub releases: ndbg-linux-x64, ndbg-darwin-arm64, etc.
|
|
822
|
+
curl -fsSL https://github.com/<org>/ndbg/releases/latest/download/ndbg-$(uname -s)-$(uname -m) -o /usr/local/bin/ndbg
|
|
823
|
+
chmod +x /usr/local/bin/ndbg
|
|
824
|
+
```
|
|
825
|
+
|
|
826
|
+
### 10.2 Skill installation (for Claude Code and compatible agents)
|
|
827
|
+
|
|
828
|
+
```bash
|
|
829
|
+
# Via vercel skills CLI
|
|
830
|
+
npx skills add <org>/ndbg
|
|
831
|
+
|
|
832
|
+
# Manual: copy SKILL.md to Claude Code skills directory
|
|
833
|
+
cp node_modules/ndbg/skills/node-debugger/SKILL.md ~/.claude/skills/node-debugger/SKILL.md
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
### 10.3 Requirements
|
|
837
|
+
|
|
838
|
+
- **Debugged process**: Node.js 16+ (for V8 inspector support)
|
|
839
|
+
- **ndbg binary**: no runtime dependency (standalone compiled binary)
|
|
840
|
+
- **ndbg via npm/npx**: Bun or Node.js 18+ on the host
|
|
841
|
+
|
|
842
|
+
---
|
|
843
|
+
|
|
844
|
+
## 11. Scope & Non-Goals
|
|
845
|
+
|
|
846
|
+
### 11.1 In scope
|
|
847
|
+
|
|
848
|
+
- Node.js / JavaScript / TypeScript debugging via V8 Inspector Protocol
|
|
849
|
+
- CLI interface optimized for AI agents
|
|
850
|
+
- CPU profiling, heap profiling, code coverage
|
|
851
|
+
- Source map support
|
|
852
|
+
- Live code editing (hotpatch)
|
|
853
|
+
- Multi-session debugging
|
|
854
|
+
|
|
855
|
+
### 11.2 Out of scope (v1)
|
|
856
|
+
|
|
857
|
+
- **Browser debugging** — ndbg targets Node.js processes only (no DOM, no CSS)
|
|
858
|
+
- **Other languages** — no Python, Rust, Go, etc. (use dedicated tools)
|
|
859
|
+
- **GUI** — no TUI, no web UI (output is text for terminal/agent consumption)
|
|
860
|
+
- **MCP server mode** — may be added later as an optional adapter, but CLI is primary
|
|
861
|
+
- **Remote debugging** — v1 targets localhost only (SSH tunneling is the recommended approach for remote)
|
|
862
|
+
- **Recording/replay** — time-travel debugging (may be a v2 feature)
|
|
863
|
+
- **Test framework integration** — ndbg debugs any Node.js process; it doesn't know about Jest/Vitest internals (but can debug them)
|
|
864
|
+
|
|
865
|
+
---
|
|
866
|
+
|
|
867
|
+
## 12. V8 Inspector Protocol Mapping
|
|
868
|
+
|
|
869
|
+
Reference for implementors. Maps ndbg commands to CDP methods.
|
|
870
|
+
|
|
871
|
+
| ndbg command | CDP domain | CDP method(s) |
|
|
872
|
+
|---|---|---|
|
|
873
|
+
| `launch --brk` | — | Spawns `node --inspect-brk` + `Runtime.runIfWaitingForDebugger` |
|
|
874
|
+
| `break file:line` | Debugger | `setBreakpointByUrl` |
|
|
875
|
+
| `break --pattern` | Debugger | `setBreakpointByUrl` (urlRegex) |
|
|
876
|
+
| `break-fn` | Debugger | `setBreakpointOnFunctionCall` |
|
|
877
|
+
| `break-on-load` | Debugger | `setInstrumentationBreakpoint` |
|
|
878
|
+
| `break-rm` | Debugger | `removeBreakpoint` |
|
|
879
|
+
| `break-toggle` | Debugger | `setBreakpointsActive` |
|
|
880
|
+
| `breakable` | Debugger | `getPossibleBreakpoints` |
|
|
881
|
+
| `catch` | Debugger | `setPauseOnExceptions` |
|
|
882
|
+
| `continue` | Debugger | `resume` |
|
|
883
|
+
| `step over` | Debugger | `stepOver` |
|
|
884
|
+
| `step into` | Debugger | `stepInto` (+ `breakOnAsyncCall`) |
|
|
885
|
+
| `step out` | Debugger | `stepOut` |
|
|
886
|
+
| `run-to` | Debugger | `continueToLocation` |
|
|
887
|
+
| `restart-frame` | Debugger | `restartFrame` |
|
|
888
|
+
| `pause` | Debugger | `pause` |
|
|
889
|
+
| `eval` | Debugger | `evaluateOnCallFrame` (awaitPromise) |
|
|
890
|
+
| `vars` | Debugger | scope chain from `paused` event → `getProperties` |
|
|
891
|
+
| `stack` | Debugger | `paused` event callFrames + `getStackTrace` |
|
|
892
|
+
| `props` | Runtime | `getProperties` |
|
|
893
|
+
| `instances` | Runtime | `queryObjects` |
|
|
894
|
+
| `globals` | Runtime | `globalLexicalScopeNames` |
|
|
895
|
+
| `search` | Debugger | `searchInContent` |
|
|
896
|
+
| `scripts` | Debugger | `scriptParsed` events (cached) |
|
|
897
|
+
| `set` | Debugger | `setVariableValue` |
|
|
898
|
+
| `set-return` | Debugger | `setReturnValue` |
|
|
899
|
+
| `hotpatch` | Debugger | `setScriptSource` |
|
|
900
|
+
| `blackbox` | Debugger | `setBlackboxPatterns` |
|
|
901
|
+
| `source` | Debugger | `getScriptSource` |
|
|
902
|
+
| `kill-execution` | Runtime | `terminateExecution` |
|
|
903
|
+
| `console` | Runtime | `consoleAPICalled` event |
|
|
904
|
+
| `exceptions` | Runtime | `exceptionThrown` event |
|
|
905
|
+
| `contexts` | Runtime | `executionContextCreated` event |
|
|
906
|
+
| `inject-hook` | Runtime | `addBinding` |
|
|
907
|
+
| `heap usage` | Runtime | `getHeapUsage` |
|
|
908
|
+
| `cpu start/stop` | Profiler | `start` / `stop` |
|
|
909
|
+
| `coverage start/stop` | Profiler | `startPreciseCoverage` / `takePreciseCoverage` |
|
|
910
|
+
| `heap snapshot` | HeapProfiler | `takeHeapSnapshot` |
|
|
911
|
+
| `heap sample start/stop` | HeapProfiler | `startSampling` / `stopSampling` |
|
|
912
|
+
| `heap track start/stop` | HeapProfiler | `startTrackingHeapObjects` / `stopTrackingHeapObjects` |
|
|
913
|
+
| `heap inspect` | HeapProfiler | `getObjectByHeapObjectId` |
|
|
914
|
+
| `gc` | HeapProfiler | `collectGarbage` |
|
|
915
|
+
| `async-depth` | Debugger | `setAsyncCallStackDepth` |
|
|
916
|
+
|
|
917
|
+
---
|
|
918
|
+
|
|
919
|
+
## 13. Testing Strategy
|
|
920
|
+
|
|
921
|
+
### 13.1 Unit tests
|
|
922
|
+
|
|
923
|
+
- Ref system: creation, resolution, lifecycle, collision handling
|
|
924
|
+
- Output formatters: variable formatting, truncation, source display
|
|
925
|
+
- Source map resolution: .ts → .js mapping, inline source maps, missing maps
|
|
926
|
+
- Command argument parsing
|
|
927
|
+
|
|
928
|
+
### 13.2 Integration tests
|
|
929
|
+
|
|
930
|
+
- Launch + attach + breakpoint + step + inspect + stop lifecycle
|
|
931
|
+
- Conditional breakpoints with expression evaluation
|
|
932
|
+
- Logpoint emission and flood throttling
|
|
933
|
+
- Source map resolution end-to-end (TypeScript project)
|
|
934
|
+
- Hotpatch: edit, verify, dry-run, blocked scenarios
|
|
935
|
+
- Multi-session: two concurrent debug sessions
|
|
936
|
+
- Heap snapshot, diff, sampling
|
|
937
|
+
- CPU profiling and coverage collection
|
|
938
|
+
- Daemon crash recovery and orphan cleanup
|
|
939
|
+
|
|
940
|
+
### 13.3 Agent simulation tests
|
|
941
|
+
|
|
942
|
+
- Scripted debugging scenarios (like the race condition / circular dependency / memory leak scenarios from the design phase) run as end-to-end bash scripts
|
|
943
|
+
- Measure: total tool calls needed, total tokens in output, success rate
|
|
944
|
+
- Compare against equivalent MCP-based debuggers
|
|
945
|
+
|
|
946
|
+
---
|
|
947
|
+
|
|
948
|
+
## 14. Future Considerations (v2+)
|
|
949
|
+
|
|
950
|
+
- **MCP adapter** — optional MCP server mode wrapping the CLI for clients that prefer MCP
|
|
951
|
+
- **Watch expressions** — persistent expressions re-evaluated on every pause
|
|
952
|
+
- **Conditional logpoint templates** — more complex logpoint logic
|
|
953
|
+
- **Time-travel debugging** — record execution and replay forward/backward
|
|
954
|
+
- **Remote debugging** — built-in SSH tunnel management
|
|
955
|
+
- **Multi-process** — debug parent + child processes (fork/cluster)
|
|
956
|
+
- **Worker threads** — attach to worker threads via `Target` domain
|
|
957
|
+
- **Flamegraph export** — export CPU profiles as interactive flamegraphs
|
|
958
|
+
- **VS Code extension** — thin wrapper that delegates to the CLI
|