SKILL.md 6.9 KB

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:

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:

python -m pdb path/to/script.py arg1 arg2

Debug pytest test:

pytest tests/foo.py::test_bar --pdb -p no:xdist   # xdist breaks pdb

Post-mortem:

import pdb, sys
try:
    run_the_thing()
except Exception:
    pdb.post_mortem(sys.exc_info()[2])

Remote debug with remote-pdb (recommended for agents):

pip install remote-pdb
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):

pip install debugpy
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:

python -m debugpy --listen 127.0.0.1:5678 --pid <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

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

kill -SIGUSR1 <pid>          # enable inspector on existing process
node inspect -p <pid>        # attach by PID
node inspect ws://127.0.0.1:9229/<uuid>   # attach by URL

Start with inspector:

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:

node --inspect-brk --import tsx script.ts

Programmatic CDP

npm i -g chrome-remote-interface
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:

cd ui-tui && npm run build
node --inspect-brk dist/entry.js
# In another terminal: node inspect -p <pid>

Running hermes --tui:

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/<uuid>

Vitest under debugger:

node --inspect-brk ./node_modules/vitest/vitest.mjs run --no-file-parallelism src/app/foo.test.tsx

Heap / CPU profiles (non-interactive)

// 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 <code>references/agent-bridge-auth-troubleshooting.md</code> for the full diagnostic and fix recipe.