The 21 Latest AI Agent Skills, Part 3: The Order of Catching Bugs and Pinning Down Python and Node
Part 2 covered delegating code and receiving verification. Part 3 is what comes next. When something goes wrong. One skill that sets the order, one that pins down Python, and one that pins down Node.
The three sit at different layers. The first is a method, and the other two are tools. With tools but no method, you do not know where to stop; with a method but no tools, you have nowhere to stop.
1. systematic-debugging (v1.1.0)
The software-development category. Taken from obra/superpowers.
4-phase root cause debugging: understand bugs before fixing.
The description carries a subtitle. Understand before fixing. That is all.
It starts with a warning from the first line
Random fixes waste time and create new bugs. Quick patches mask underlying issues.
Random fixing wastes time and creates new bugs. A hasty patch covers up the real problem. Having this sentence at the very front is the character of this skill.
Core principle: ALWAYS find root cause before attempting fixes.
Symptom fixes are failure.
Eliminating the symptom is failure. This is not logic — it is the accident this skill actually prevents. It is the behavior the agent most often engages in. It sees an error message and immediately starts touching things to try a rough fix.
Iron Law
NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
It is written as an iron law. Until Phase 1 is finished, it does not offer a fix proposal at all. The moment "just try changing this" goes out, the skill collapses. And one line is attached.
Violating the letter of this process is violating the spirit of debugging.
It is the same sentence as the TDD skill in Part 2. Keep the letter of the rule, but do not break its spirit. If you formally went through the 4 phases but did not actually find the cause, then you did not go through the 4 phases — you only wrote the letters.
Feedback Loop Rule
This is the most practical part of this skill. Before reading the code and guessing the cause, you must first build a narrow, fast command that turns the symptom the user saw red.
The loop must satisfy four conditions.
1. One command reproduces the exact symptom
2. It fails only on this bug, and passes once fixed
3. It is fast enough to run repeatedly
4. It is deterministic — the same result every time
If reproduction is hard, build the loop even if it takes longer. It explicitly says not to guess. This is exactly the failure mode this skill tries to prevent. "Judging" the cause without a loop.
There is an order when building the loop.
1. A failing test at the seam that reaches the bug (unit/integration/E2E)
2. An HTTP script or curl against a running dev server
If reproduction is impossible at all, gather more data. Again, no guessing.
Moments you must use it
- Time pressure (the moment guessing becomes tempting)
- "One quick fix should do it"
- You already tried several moves and
- The previous repair failed and
- You do not fully understand the problem
Moments you must not skip it
- When the issue looks simple (even simple bugs have root causes)
- When you are busy
- When there is pressure to fix it right now
The last item is the most realistic. Being busy is exactly when this skill is needed. When you are busy, you want to patch it roughly and move on.
2. python-debugpy (v1.0.0)
The software-development category.
Debug Python: pdb REPL + debugpy remote (DAP).
It splits three tools by situation.
| Tool | When |
|---|---|
breakpoint() + pdb | Local, interactive, simplest. Put it in the source and just run; you get a REPL at that line |
python -m pdb | Run an existing script with pdb without editing the source |
debugpy | Remote/headless, attach to an already running process. Speaks DAP and can be scripted from a terminal, essential for long-lived processes like gateways and daemons |
The skill gives an instruction.
Start with breakpoint(). It's the cheapest thing that works.
Start with the cheapest thing that works. The important part is that "cheapest" is the criterion. If you pull out a complex tool first, the time spent learning that tool is wasted from the debugging time.
When to use it
- When a test failed but the traceback alone does not show why the value is wrong
- When you want to step into a function line by line and watch a collection change
- When a long-lived process like a gateway or tui_gateway misbehaves and cannot be restarted
- When you want to see the local variables at the exception point that blew up in production
- When a subprocess (_SlashWorker, PTY bridge worker) is the real bug site
The last two items are the reason this skill exists. Most of the problems actually experienced in Hermes are here. The parent process is fine, but the child process misbehaves and nothing is printed to the parent log.
When not to use it is also specified.
Don't use for: things solved in a minute with print()/logging.debug,
things pytest -vv --tb=long --showlocals already shows
pdb commands
Picking only the most frequently used:
n step over s step into r step out of function c continue
w call stack u / d up/down the stack a print current function args
p expr print value pp expr pretty-print l / ll surrounding source
b file:line break b file:line, cond conditional interact REPL in current scope
interact is the most powerful here. In the current scope you can import, inspect complex objects, and even call methods that change state. But locals are read-only by default, so you have to do !x = 42 to change them. There was a time I did not know this and wandered around saying "the change isn't taking."
post-mortem
When you want to look at the point after the exception has already blown up.
import pdb, sys
try:
run_the_thing()
except Exception:
pdb.post_mortem(sys.exc_info()[2])
There is also a way to wrap the whole script.
python -m pdb -c continue script.py
-c continue is the key. Do not stop at the first line; go along, and the moment it blows up, catch it in that frame. In a REPL/Jupyter you can also set it globally with a sys.excepthook hook.
Cautions when using debugpy
Debug the gateway not in production but in a separate dev checkout and a separate data home. The order is fixed.
source ./activate
python -m pm.build_env --source . --out .venv --group dev --group test
.venv/bin/python -c "import debugpy; print(debugpy.__file__)"
The problem is that it is not in the dev group. If you use all, debugpy does not come in. And there must not already be an output. Stop and delete the process for that environment, then recreate it. There is a warning that you must not add debugpy to the production environment, and this is not trivial — it is production contamination.
3. node-inspect-debugger (v1.0.0)
The same structure, the counterpart to the Python skill. node inspect is the default, and CDP is used when automation is needed.
Debug Node.js via --inspect + Chrome DevTools Protocol CLI.
Tool selection enforces the same priority.
Prefer node inspect first. It's always available and the REPL is fast.
When to use it
- When a Node test fails and you need to see intermediate state
- When ui-tui crashes or misbehaves (to see the pre-render React/Ink state)
- When a tui_gateway child process (_SlashWorker, PTY bridge) misbehaves
- When you need a value inside a closure that console.log cannot reach
- When attaching to a running process to take a CPU profile or heap snapshot
The fourth item is quietly important. To print a value inside a closure with console.log, you have to modify the source or work around it. That is source contamination. A breakpoint does not touch the source.
Grabbing a running process
This is the practical core of this skill. How to attach to a gateway that is already running.
# 1. Send SIGUSR1 to the running process to turn on the inspector
kill -SIGUSR1 <pid>
# Node prints: Debugger listening on ws://127.0.0.1:9229/<uuid>
# 2. Attach
node inspect -p <pid>
# Or by URL
node inspect ws://127.0.0.1:9229/<uuid>
Attaching without a restart is why this is needed. If you want to turn on the inspector from the start:
node --inspect script.js # waits at 127.0.0.1:9229, keeps running
node --inspect-brk script.js # waits + stops at the first line
node --inspect=0.0.0.0:9230 script.js # specify host·port
TypeScript needs tsx inserted.
node --inspect-brk --import tsx script.ts
# For older tsx
node --inspect-brk -r tsx/cjs script.ts
The difference between --inspect and --inspect-brk is confusing in practice. If you only want it to wait without stopping, use --inspect; if you want it to stop at the first line and inspect internal state, use --inspect-brk. If you want to check from the first line regardless of where breakpoints are set, -brk is convenient.
REPL commands
The prompt is debug>.
c / cont continue n / next one line s / step into o / out step out
sb('file.js', 42) source-line breakpoint sb(42) line in current file sb('functionName')
cb('file.js', 42) clear breakpoint breakpoints list
bt call stack list(5) surrounding 5 lines watch('expr') evaluate each time
repl REPL in current scope (exit with Ctrl+C) exec expr evaluate once
restart / kill / .exit
The repl sub-mode is the core. There you get direct access to locals and closure variables. It is the counterpart to Python's interact. Ctrl+C returns you to debug>.
If setting breakpoints by hand one at a time is tedious, script CDP with chrome-remote-interface. When you want an agent loop to automatically set several breakpoints and collect state across multiple runs, this is the right approach.
Part 3 Summary
| Skill | Version | What it does |
|---|---|---|
| systematic-debugging | 1.1.0 | 4 phases starting from the root cause |
| python-debugpy | 1.0.0 | pdb REPL, post-mortem, debugpy remote attach |
| node-inspect-debugger | 1.0.0 | node inspect REPL, SIGUSR1 attach, CDP automation |
The three form a single axis. Set the order, stop Python, stop Node. If you set a breakpoint without an order, you see nothing there.
Next, Part 4 moves on to checking code before committing.
AI Knowledge Hub