--- name: debugging-toolkit description: | Debug Python and Node.js applications using pdb, debugpy, remote-pdb, node inspect, and Chrome DevTools Protocol. Covers local breakpoints, remote attach, post-mortem, and Hermes-specific processes. version: 1.0.0 platforms: [linux, macos, windows] metadata: hermes: tags: [debugging, python, nodejs, pdb, debugpy, cdp, breakpoints] --- # Debugging Toolkit ## Python Debugging Three tools, pick by situation: | Tool | When | |------|------| | `breakpoint()` + pdb | Local, interactive, simplest | | `python -m pdb` | Launch script under debugger with no source edits | | `debugpy` / `remote-pdb` | Remote / headless / attach to running process | ### pdb Quick Reference ``` n next line (step over) s step into r return from current function c continue unt N continue until line N j N jump to line N l / ll list source / full function w where (stack trace) u / d up / down stack p expr print expression pp expr pretty-print display expr auto-print on every stop b file:line set breakpoint b func break on function entry interact drop into full Python REPL in current scope q quit ``` ### Recipes **Local breakpoint:** ```python def compute(x, y): result = some_helper(x) breakpoint() # drops into pdb here return result + y ``` Remove before committing: `rg -n 'breakpoint\(\)' --type py` **Launch under pdb:** ```bash python -m pdb path/to/script.py arg1 arg2 ``` **Debug pytest test:** ```bash pytest tests/foo.py::test_bar --pdb -p no:xdist # xdist breaks pdb ``` **Post-mortem:** ```python import pdb, sys try: run_the_thing() except Exception: pdb.post_mortem(sys.exc_info()[2]) ``` **Remote debug with remote-pdb (recommended for agents):** ```bash pip install remote-pdb ``` ```python from remote_pdb import set_trace set_trace(host="127.0.0.1", port=4444) # blocks until connection ``` Then: `nc 127.0.0.1 4444` → full `(Pdb)` prompt. **Remote debug with debugpy (IDE integration):** ```bash pip install debugpy ``` ```python import debugpy debugpy.listen(("127.0.0.1", 5678)) debugpy.wait_for_client() debugpy.breakpoint() ``` Or launch: `python -m debugpy --listen 127.0.0.1:5678 --wait-for-client script.py` Attach from VS Code with `request: attach` launch config. **Attach to running process:** ```bash python -m debugpy --listen 127.0.0.1:5678 --pid ``` May need: `echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope` ### Hermes-specific Python processes - **Tests:** always add `-p no:xdist` for pdb - **Gateway / tui_gateway:** use `remote-pdb` at handler entry - **_SlashWorker:** `set_trace()` inside worker's exec path - **Asyncio:** pdb works in coroutines; `await` inside pdb requires Python 3.13+ --- ## Node.js Debugging Two tools: | Tool | When | |------|------| | `node inspect` | Built-in CLI REPL, quick poking | | CDP via `chrome-remote-interface` | Scriptable automation, non-interactive | ### `node inspect` Quick Reference ```bash node inspect path/to/script.js node --inspect-brk $(which tsx) path/to/script.ts ``` ``` c / cont continue n / next step over s / step step into o / out step out sb('file.js', 42) set breakpoint cb('file.js', 42) clear breakpoint bt backtrace list(5) show 5 lines around current repl drop into JS REPL in current scope exec expr evaluate expression restart restart script .exit quit ``` ### Attaching to running process ```bash kill -SIGUSR1 # enable inspector on existing process node inspect -p # attach by PID node inspect ws://127.0.0.1:9229/ # attach by URL ``` Start with inspector: ```bash node --inspect script.js # listen on 9229, keep running node --inspect-brk script.js # listen AND pause on first line node --inspect=0.0.0.0:9230 script.js # custom host:port ``` TypeScript via tsx: ```bash node --inspect-brk --import tsx script.ts ``` ### Programmatic CDP ```bash npm i -g chrome-remote-interface ``` ```javascript const CDP = require('chrome-remote-interface'); (async () => { const client = await CDP({ port: 9229 }); const { Debugger, Runtime } = client; Debugger.paused(async ({ callFrames }) => { const top = callFrames[0]; console.log(`PAUSED @ ${top.url}:${top.location.lineNumber + 1}`); // ... inspect scopes, evaluate expressions, resume }); await Runtime.enable(); await Debugger.enable(); await Debugger.setBreakpointByUrl({ urlRegex: '.*app\\.tsx$', lineNumber: 119 }); await Runtime.runIfWaitingForDebugger(); })(); ``` ### Hermes ui-tui debugging **Single component under dev:** ```bash cd ui-tui && npm run build node --inspect-brk dist/entry.js # In another terminal: node inspect -p ``` **Running `hermes --tui`:** ```bash hermes --tui & TUI_PID=$(pgrep -f 'ui-tui/dist/entry' | head -1) kill -SIGUSR1 "$TUI_PID" curl -s http://127.0.0.1:9229/json/list | jq -r '.[0].webSocketDebuggerUrl' node inspect ws://127.0.0.1:9229/ ``` **Vitest under debugger:** ```bash node --inspect-brk ./node_modules/vitest/vitest.mjs run --no-file-parallelism src/app/foo.test.tsx ``` ### Heap / CPU profiles (non-interactive) ```javascript // CPU profile await client.Profiler.enable(); await client.Profiler.start(); await new Promise(r => setTimeout(r, 5000)); const { profile } = await client.Profiler.stop(); fs.writeFileSync('/tmp/cpu.cpuprofile', JSON.stringify(profile)); // Heap snapshot await client.HeapProfiler.enable(); const chunks = []; client.HeapProfiler.addHeapSnapshotChunk(({ chunk }) => chunks.push(chunk)); await client.HeapProfiler.takeHeapSnapshot({ reportProgress: false }); fs.writeFileSync('/tmp/heap.heapsnapshot', chunks.join('')); ``` --- ## Common Pitfalls | Issue | Fix | |-------|-----| | pdb under pytest-xdist silently hangs | Use `-p no:xdist` or `-n 0` | | `breakpoint()` in CI hangs | Never commit breakpoints; pre-commit grep | | `PYTHONBREAKPOINT=0` disables breakpoints | Check env var | | debugpy without `wait_for_client()` races past first breakpoint | Add `wait_for_client()` | | Attach to PID fails on hardened kernels | `echo 0 > /proc/sys/kernel/yama/ptrace_scope` | | Node breakpoints hit emitted JS not TS | Break in `dist/*.js` or enable sourcemaps | | `--inspect` vs `--inspect-brk` | Use `-brk` when you need to set breakpoints before code runs | | Port collisions on 9229 | Use `--inspect=0` for random port | | `--inspect` on parent doesn't inspect children | Use `NODE_OPTIONS='--inspect-brk'` | | Background `node inspect` target stays paused after Ctrl+C | `cont` first or `kill` explicitly | ## Agent Bridge Authentication Failures When an agent bridge (e.g., Claude Code → Feishu) reports `Agent error` or `SEND_TO_CC` failure, the real error is usually an expired Claude API token, not a bridge bug. See [`references/agent-bridge-auth-troubleshooting.md`](references/agent-bridge-auth-troubleshooting.md) for the full diagnostic and fix recipe.