---
name: language-debuggers
description: "Debug Python (pdb + debugpy DAP) and Node.js (--inspect + CDP CLI) from the terminal, plus systematic debugging methodology, code review prep, simplification, and spike patterns."
version: 2.0.0
author: Hermes Agent
license: MIT
platforms: [linux, macos, windows]
metadata:
  hermes:
    tags: [debugging, python, pdb, debugpy, nodejs, node-inspect, cdp, breakpoints, dap, troubleshooting, root-cause, spike]
    related_skills: [software-quality-practices, plan, github-pr-workflow]
---

# Language Debuggers

Debug Python and Node.js code from the terminal. Covers interactive REPL debugging, remote DAP debugging, and programmatic Chrome DevTools Protocol (CDP) control.

> **Load this skill when the user asks to debug, set breakpoints, step through code, or inspect runtime state in Python or Node.js.**

---

## 1. Python Debugging (pdb + debugpy)

### Tool 1: pdb — Interactive REPL

Python's built-in debugger. No dependencies. Use for quick breakpoint insertion.

#### Breakpoint Entry Methods

| Method | Code | When to use |
|--------|------|-------------|
| `breakpoint()` | Insert in source | Persistent, checked into code |
| `pdb.set_trace()` | Same as above | Legacy Python |
| `-m pdb script.py` | Command-line | Debug without modifying source |
| `python -m pdb -c continue script.py` | Auto-continue to first bp | Skip the initial `(Pdb)` prompt |

```bash
# Debug a script from the start
python -m pdb script.py

# Debug with auto-continue to first breakpoint
python -m pdb -c continue script.py

# Debug a module
python -m pdb -m mypackage.mymodule
```

#### pdb Commands

| Command | Shortcut | Action |
|---------|----------|--------|
| `help` | `h` | Show commands |
| `step` | `s` | Step into function call |
| `next` | `n` | Step over (next line) |
| `until` | `unt` | Continue until line (out of loop) |
| `continue` | `c` | Continue to next breakpoint |
| `where` | `w` | Print stack trace |
| `list` | `l` | Show source around current line |
| `args` | `a` | Show argument values |
| `pp expr` | — | Pretty-print expression |
| `display expr` | — | Auto-print expression after each step |
| `quit` | `q` | Exit debugger (raises BdbQuit) |

```python
# Inside pdb:
(Pdb) pp my_complex_dict          # pretty-print
(Pdb) display len(my_list)       # show list length after each step
(Pdb) !x = 5                     # execute assignment (! prefix)
(Pdb) w                          # see call stack
```

#### Conditional Breakpoints

```python
# Break only when condition is true
import pdb; pdb.set_trace()  # then: condition i > 100

# Or inline
if some_condition:
    breakpoint()
```

### Tool 2: debugpy — Remote DAP Debugging

VS Code's debug adapter protocol (DAP) for remote debugging.

#### Setup (listen mode — typical remote scenario)

```python
# In the script to debug (or __main__ block)
import debugpy

debugpy.listen(("0.0.0.0", 5678))    # listen on all interfaces
print("Waiting for debugger to attach on port 5678...")
debugpy.wait_for_client()            # block until VS Code connects

debugpy.breakpoint()                  # explicit breakpoint after attach
```

#### VS Code Launch Config

```json
{
  "name": "Attach to Python",
  "type": "debugpy",
  "request": "attach",
  "connect": {
    "host": "remote-host-ip",
    "port": 5678
  },
  "pathMappings": [
    {
      "localRoot": "${workspaceFolder}",
      "remoteRoot": "/app"
    }
  ]
}
```

#### Connect Mode (alternative)

```python
# Script connects OUT to debugger
import debugpy
debugpy.connect(("debugger-host", 5678))
debugpy.breakpoint()
```

### Tool 3: Post-Mortem Debugging

Inspect crashed programs after the exception:

```python
import pdb, sys

try:
    risky_operation()
except Exception:
    pdb.post_mortem(sys.exc_info()[2])
    # Drops into pdb at the exception point
```

```bash
# Python auto-post-mortem (PYTHONBREAKPOINT=pdb.pm is unreliable)
python -c "import pdb, sys; try: ...; except: pdb.post_mortem()"
```

---

## 2. Node.js Debugging (--inspect + CDP CLI)

### Method 1: Interactive Inspect REPL

```bash
# Start with inspector
node --inspect script.js              # starts inspector, doesn't break
node --inspect-brk script.js          # breaks on first line

# In another terminal, connect the CLI debugger
node inspect localhost:9229           # or chrome://inspect in browser
```

#### Inspector CLI Commands

| Command | Action |
|---------|--------|
| `cont` / `c` | Continue |
| `next` / `n` | Step over |
| `step` / `s` | Step into |
| `out` / `o` | Step out |
| `setBreakpoint(10)` / `sb(10)` | Break at line 10 |
| `clearBreakpoint(10)` / `cb(10)` | Remove breakpoint |
| `watch('expr')` | Watch expression |
| `exec expr` | Evaluate expression |
| `repl` | Enter interactive JS repl |

```bash
$ node inspect localhost:9229
debug> sb(25)
debug> c
debug> exec Object.keys(myObj)
debug> repl
> myVar + 1
```

### Method 2: CDP CLI — Programmatic Control

Drive the Chrome DevTools Protocol from the terminal for unattended debugging.

```bash
npm install -g chrome-remote-interface
```

#### Basic Script

```javascript
const CDP = require('chrome-remote-interface');

async function debugScript(port) {
  const client = await CDP({ port });
  const { Runtime, Debugger } = client;

  // Enable domains
  await Runtime.enable();
  await Debugger.enable();

  // Set breakpoint
  await Debugger.setBreakpointByUrl({
    lineNumber: 42,
    urlRegex: '.*script\.js$'
  });

  // Breakpoint hit handler
  Debugger.paused(async (params) => {
    console.log('Paused at:', params.callFrames[0].location);

    // Get variable values
    const result = await Runtime.evaluate({
      expression: 'Object.keys(global)',
      returnByValue: true
    });
    console.log('Globals:', result.result.value);

    // Resume
    await Debugger.resume();
  });

  console.log(`Debugger attached to port ${port}`);
}

debugScript(9229);
```

### Method 3: Browser DevTools

```bash
node --inspect script.js
# Then open chrome://inspect → click "inspect" on the target
```

### Method 4: VS Code — Launch Config

```json
{
  "type": "node",
  "request": "launch",
  "name": "Launch Program",
  "program": "${workspaceFolder}/script.js",
  "runtimeArgs": ["--inspect"]
}
```

Or attach to an existing process:

```json
{
  "type": "node",
  "request": "attach",
  "name": "Attach to Port",
  "port": 9229,
  "address": "localhost",
  "restart": true
}
```

### Method 5: JetBrains IDE

1. `node --inspect script.js`
2. IDE → Run → Attach to Node.js → specify host:port

### Inspector Protocol Notes

- **WebSocket endpoint:** `ws://127.0.0.1:9229/unique-session-id`
- **JSON endpoint:** `http://127.0.0.1:9229/json/list`
- **PID association:** endpoint JSON includes `title: "node script.js pid=1234"` for process identification

### Common Gotchas

| Issue | Solution |
|-------|----------|
| CLI doesn't echo typed text | It's by design; use tab-completion and don't worry |
| `node inspect` skips source maps | Compile TS to JS first with source maps enabled |
| WS endpoint not responding | `curl http://localhost:9229/json/list` to verify |
| Multiple Node processes | Use `--inspect-port=9230` to avoid collisions |
| Script exits before debugger attaches | Use `--inspect-brk` instead of `--inspect` |
| Docker port mapping | Map 9229 from container to host, use `0.0.0.0:9229` in container |
| Breakpoint on wrong line | Node uses 0-indexed columns, 1-indexed lines (1,1 = start of file) |

---

## Cross-Language Patterns

| Concept | Python (pdb/debugpy) | Node.js (inspect/CDP) |
|---------|---------------------|----------------------|
| Insert breakpoint | `breakpoint()` / `pdb.set_trace()` | `debugger;` statement |
| CLI entry | `python -m pdb script.py` | `node inspect script.js` |
| Remote attach | `debugpy.listen((host, port))` | `node --inspect=host:port` |
| Step into | `step` / `s` | `step` / `s` |
| Step over | `next` / `n` | `next` / `n` |
| Continue | `continue` / `c` | `cont` / `c` |
| Stack trace | `where` / `w` | `backtrace` / `bt` |
| Evaluate expression | `pp expr` | `exec expr` / `repl` |
| Post-mortem | `pdb.post_mortem(tb)` | `node --inspect-brk` + manual |
| List source | `list` / `l` | `list(N)` |

---

## Debugging Methodology References

These archived references live in `references/` for deeper reading:

- `references/systematic-debugging.md` — 4-phase root-cause methodology (no fixes before root cause)
- `references/requesting-code-review.md` — How to package a PR/diff for review after debugging
- `references/simplify-code.md` — Code simplification heuristics that often reveal bugs
- `references/spike.md` — Time-boxed exploration for uncertain bug hunts

## Workflow: Debug a Failing Script

```
1. Identify the failing file and line from traceback
2. Insert breakpoint: Python → breakpoint() at suspect line
                     Node.js → debugger; at suspect line
3. Run with debugger attached:
   Python → python -m pdb -c continue script.py
   Node.js → node --inspect-brk script.js
4. Step through (s/n/c) until anomaly found
5. Inspect variables with pp/exec
6. Fix and re-run tests
7. Remove breakpoints before committing
```
