[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6JF5BEG6R7A0DYRFVB2HBF2":3,"skill-same:01M6JF5BEG6R7A0DYRFVB2HBF2":83},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":27,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":61,"commitSha":62,"skillDirectory":63,"forksCount":64,"repositoryHtmlUrl":65,"repositoryHomepageUrl":66,"ownerHtmlUrl":67,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":68,"install":69,"resources":82},"01M6JF5BEG6R7A0DYRFVB2HBF2","python-debugpy",null,"Debug Python: pdb REPL + debugpy remote (DAP).","S3YED\u002Fappie-kit","S3YED","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F62743867?v=4","01M6JBKBRRVP6KMS0VWZ27T3R6","01M6JBKBX0KG30284PW1DBYJT1",14,"MIT","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fops\u002Fpython-debugpy\u002FSKILL.md",[17,22],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",{"categoryNo":23,"categoryName":24,"taxonomyType":25,"taxonomyTypeDesc":26},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"python","python debugpy","debug","debugging","debugger","hermes","hermes agent","node","tui","commands","systematic","pdb","post","remote","inspect","systematic-debugging","2026-11-10 10:12:30",false,"---\nname: python-debugpy\ndescription: \"Debug Python: pdb REPL + debugpy remote (DAP).\"\nversion: 1.0.0\nauthor: Hermes Agent\nlicense: MIT\nmetadata:\n  hermes:\n    tags: [debugging, python, pdb, debugpy, breakpoints, dap, post-mortem]\n    related_skills: [systematic-debugging, node-inspect-debugger, debugging-hermes-tui-commands]\n---\n\n# Python Debugger (pdb + debugpy)\n\n## Overview\n\nThree tools, picked by situation:\n\n| Tool | When |\n|---|---|\n| **`breakpoint()` + pdb** | Local, interactive, simplest. Add `breakpoint()` in the source, run normally, get a REPL at that line. |\n| **`python -m pdb`** | Launch an existing script under pdb with no source edits. Useful for quick poking. |\n| **`debugpy`** | Remote \u002F headless \u002F \"attach to already-running process.\" Talks DAP, scriptable from terminal, works for long-lived processes (gateway, daemon, PTY children). |\n\n**Start with `breakpoint()`.** It's the cheapest thing that works.\n\n## When to Use\n\n- A test fails and the traceback doesn't reveal why a value is wrong\n- You need to step through a function and watch a collection mutate\n- A long-running process (hermes gateway, tui_gateway) misbehaves and you can't restart it\n- Post-mortem: an exception fired in prod-ish code and you want to inspect locals at the crash site\n- A subprocess \u002F child (Python `_SlashWorker`, PTY bridge worker) is the actual bug site\n\n**Don't use for:** things `print()` \u002F `logging.debug` solve in under a minute, or things `pytest -vv --tb=long --showlocals` already reveals.\n\n## pdb Quick Reference\n\nInside any pdb prompt (`(Pdb)`):\n\n| Command | Action |\n|---|---|\n| `h` \u002F `h cmd` | help |\n| `n` | next line (step over) |\n| `s` | step into |\n| `r` | return from current function |\n| `c` | continue |\n| `unt N` | continue until line N |\n| `j N` | jump to line N (same function only) |\n| `l` \u002F `ll` | list source around current line \u002F full function |\n| `w` | where (stack trace) |\n| `u` \u002F `d` | move up \u002F down in the stack |\n| `a` | print args of the current function |\n| `p expr` \u002F `pp expr` | print \u002F pretty-print expression |\n| `display expr` | auto-print expr on every stop |\n| `b file:line` | set breakpoint |\n| `b func` | break on function entry |\n| `b file:line, cond` | conditional breakpoint |\n| `cl N` | clear breakpoint N |\n| `tbreak file:line` | one-shot breakpoint |\n| `!stmt` | execute arbitrary Python (assignments included) |\n| `interact` | drop into full Python REPL in current scope (Ctrl+D to exit) |\n| `q` | quit |\n\nThe `interact` command is the most powerful — you can import anything, inspect complex objects, even call methods that mutate state. Locals are read-only by default; use `!x = 42` from the `(Pdb)` prompt to mutate.\n\n## Recipe 1: Local breakpoint\n\nEasiest. Edit the file:\n\n```python\ndef compute(x, y):\n    result = some_helper(x)\n    breakpoint()           # \u003C-- drops into pdb here\n    return result + y\n```\n\nRun the code normally. You land at the `breakpoint()` line with full access to locals.\n\n**Don't forget to remove `breakpoint()` before committing.** Use `git diff` or a pre-commit grep:\n```bash\nrg -n 'breakpoint\\(\\)' --type py\n```\n\n## Recipe 2: Launch a script under pdb (no source edits)\n\n```bash\npython -m pdb path\u002Fto\u002Fscript.py arg1 arg2\n# Lands at first line of script\n(Pdb) b path\u002Fto\u002Fscript.py:42\n(Pdb) c\n```\n\n## Recipe 3: Debug a pytest test\n\nThe hermes test runner and pytest both support this:\n\n```bash\n# Drop to pdb on failure (or on any raised exception):\nscripts\u002Frun_tests.sh tests\u002Fpath\u002Fto\u002Ftest_file.py::test_name --pdb\n\n# Drop to pdb at the START of the test:\nscripts\u002Frun_tests.sh tests\u002Fpath\u002Fto\u002Ftest_file.py::test_name --trace\n\n# Show locals in tracebacks without pdb:\nscripts\u002Frun_tests.sh tests\u002Fpath\u002Fto\u002Ftest_file.py --showlocals --tb=long\n```\n\nNote: `scripts\u002Frun_tests.sh` uses xdist (`-n 4`) by default, and pdb does NOT work under xdist. Add `-p no:xdist` or run a single test with `-n 0`:\n\n```bash\nscripts\u002Frun_tests.sh tests\u002Ffoo_test.py::test_bar --pdb -p no:xdist\n# or\nsource .venv\u002Fbin\u002Factivate\npython -m pytest tests\u002Ffoo_test.py::test_bar --pdb\n```\n\nThis bypasses the hermetic-env guarantees — fine for debugging, but re-run under the wrapper to confirm before pushing.\n\n## Recipe 4: Post-mortem on any exception\n\n```python\nimport pdb, sys\ntry:\n    run_the_thing()\nexcept Exception:\n    pdb.post_mortem(sys.exc_info()[2])\n```\n\nOr wrap a whole script:\n\n```bash\npython -m pdb -c continue script.py\n# When it crashes, pdb catches it and you're in the frame of the exception\n```\n\nOr set a global hook in a repl\u002Fjupyter:\n\n```python\nimport sys\ndef excepthook(etype, value, tb):\n    import pdb; pdb.post_mortem(tb)\nsys.excepthook = excepthook\n```\n\n## Recipe 5: Remote debug with debugpy (attach to running process)\n\nFor long-lived processes: Hermes gateway, tui_gateway, a daemon, a process that's already misbehaving and can't be restarted clean.\n\n### Setup\n\n```bash\nsource \u002Fhome\u002Fbb\u002Fhermes-agent\u002F.venv\u002Fbin\u002Factivate\npip install debugpy\n```\n\n### Pattern A: Source-edit — process waits for debugger at launch\n\nAdd near the top of the entry point (or inside the function you want to debug):\n\n```python\nimport debugpy\ndebugpy.listen((\"127.0.0.1\", 5678))\nprint(\"debugpy listening on 5678, waiting for client...\", flush=True)\ndebugpy.wait_for_client()\ndebugpy.breakpoint()       # optional: pause immediately once attached\n```\n\nStart the process; it blocks on `wait_for_client()`.\n\n### Pattern B: No source edit — launch with `-m debugpy`\n\n```bash\npython -m debugpy --listen 127.0.0.1:5678 --wait-for-client your_script.py arg1\n```\n\nEquivalent for module entry:\n\n```bash\npython -m debugpy --listen 127.0.0.1:5678 --wait-for-client -m your.module\n```\n\n### Pattern C: Attach to an already-running process\n\nNeeds the PID and debugpy preinstalled in the target's environment:\n\n```bash\npython -m debugpy --listen 127.0.0.1:5678 --pid \u003Cpid>\n# debugpy injects itself into the process. Then attach a client as below.\n```\n\nSome kernels\u002Fsecurity configs block the ptrace-based injection (`\u002Fproc\u002Fsys\u002Fkernel\u002Fyama\u002Fptrace_scope`). Fix with:\n```bash\necho 0 | sudo tee \u002Fproc\u002Fsys\u002Fkernel\u002Fyama\u002Fptrace_scope\n```\n\n### Connecting a client from the terminal\n\nThe easiest terminal-side DAP client is VS Code CLI or a small script. From inside Hermes you have two practical options:\n\n**Option 1: `debugpy`'s own CLI REPL** — not an official feature, but a tiny DAP client script:\n\n```python\n# \u002Ftmp\u002Fdap_client.py\nimport socket, json, itertools, time, sys\n\nHOST, PORT = \"127.0.0.1\", 5678\ns = socket.create_connection((HOST, PORT))\nseq = itertools.count(1)\n\ndef send(msg):\n    msg[\"seq\"] = next(seq)\n    body = json.dumps(msg).encode()\n    s.sendall(f\"Content-Length: {len(body)}\\r\\n\\r\\n\".encode() + body)\n\ndef recv():\n    header = b\"\"\n    while b\"\\r\\n\\r\\n\" not in header:\n        header += s.recv(1)\n    length = int(header.decode().split(\"Content-Length:\")[1].split(\"\\r\\n\")[0].strip())\n    body = b\"\"\n    while len(body) \u003C length:\n        body += s.recv(length - len(body))\n    return json.loads(body)\n\nsend({\"type\": \"request\", \"command\": \"initialize\", \"arguments\": {\"adapterID\": \"python\"}})\nprint(recv())\nsend({\"type\": \"request\", \"command\": \"attach\", \"arguments\": {}})\nprint(recv())\nsend({\"type\": \"request\", \"command\": \"setBreakpoints\",\n      \"arguments\": {\"source\": {\"path\": sys.argv[1]},\n                    \"breakpoints\": [{\"line\": int(sys.argv[2])}]}})\nprint(recv())\nsend({\"type\": \"request\", \"command\": \"configurationDone\"})\n# ... loop reading events and sending continue\u002FstepIn\u002Fetc.\n```\n\nThis is fine for one-off automation but painful as an interactive UX.\n\n**Option 2: Attach from VS Code \u002F Cursor \u002F Zed** — if the user has one open, they can add a `launch.json`:\n\n```json\n{\n  \"name\": \"Attach to Hermes\",\n  \"type\": \"debugpy\",\n  \"request\": \"attach\",\n  \"connect\": { \"host\": \"127.0.0.1\", \"port\": 5678 },\n  \"justMyCode\": false,\n  \"pathMappings\": [\n    { \"localRoot\": \"${workspaceFolder}\", \"remoteRoot\": \"\u002Fhome\u002Fbb\u002Fhermes-agent\" }\n  ]\n}\n```\n\n**Option 3: Ditch DAP, use `remote-pdb`** — usually what you actually want from a terminal agent:\n\n```bash\npip install remote-pdb\n```\n\nIn your code:\n```python\nfrom remote_pdb import set_trace\nset_trace(host=\"127.0.0.1\", port=4444)   # blocks until connection\n```\n\nThen from the terminal:\n```bash\nnc 127.0.0.1 4444\n# You get a (Pdb) prompt exactly as if debugging locally.\n```\n\n`remote-pdb` is the cleanest agent-friendly choice when `debugpy`'s DAP protocol is overkill. Use `debugpy` only when you actually need IDE integration.\n\n## Debugging Hermes-specific Processes\n\n### Tests\nSee Recipe 3. Always add `-p no:xdist` or run single tests without xdist.\n\n### `run_agent.py` \u002F CLI — one-shot\nEasiest: add `breakpoint()` near the suspect line, then run `hermes` normally. Control returns to your terminal at the pause point.\n\n### `tui_gateway` subprocess (spawned by `hermes --tui`)\nThe gateway runs as a child of the Node TUI. Options:\n\n**A. Source-edit the gateway:**\n```python\n# tui_gateway\u002Fserver.py near the top of serve()\nimport debugpy\ndebugpy.listen((\"127.0.0.1\", 5678))\ndebugpy.wait_for_client()\n```\nStart `hermes --tui`. The TUI will appear frozen (its backend is waiting). Attach a client; execution resumes when you `continue`.\n\n**B. Use `remote-pdb` at a specific handler:**\n```python\nfrom remote_pdb import set_trace\nset_trace(host=\"127.0.0.1\", port=4444)   # in the RPC handler you want to trap\n```\nTrigger the matching slash command from the TUI, then `nc 127.0.0.1 4444` in another terminal.\n\n### `_SlashWorker` subprocess\nSame pattern — `remote-pdb` with `set_trace()` inside the worker's `exec` path. The worker is persistent across slash commands, so the first trigger blocks until you connect; subsequent slash commands pass through normally unless you re-arm.\n\n### Gateway (`gateway\u002Frun.py`)\nLong-lived. Use `remote-pdb` at a handler, or `debugpy` with `--wait-for-client` if you're restarting the gateway anyway.\n\n## Common Pitfalls\n\n1. **pdb under pytest-xdist silently does nothing.** You won't see the prompt, the test just hangs. Always use `-p no:xdist` or `-n 0`.\n\n2. **`breakpoint()` in CI \u002F non-TTY contexts hangs the process.** Safe locally; never commit it. Add a pre-commit grep as a safety net.\n\n3. **`PYTHONBREAKPOINT=0`** disables all `breakpoint()` calls. Check the env if your breakpoint isn't hitting:\n   ```bash\n   echo $PYTHONBREAKPOINT\n   ```\n\n4. **`debugpy.listen` blocks only if you also call `wait_for_client()`.** Without it, execution continues and your first breakpoint may fire before the client is attached.\n\n5. **Attach to PID fails on hardened kernels.** `ptrace_scope=1` (Ubuntu default) allows only same-user ptrace of child processes. Workaround: `echo 0 > \u002Fproc\u002Fsys\u002Fkernel\u002Fyama\u002Fptrace_scope` (needs root) or launch under `debugpy` from the start.\n\n6. **Threads.** `pdb` only debugs the current thread. For multithreaded code, use `debugpy` (thread-aware DAP) or set `threading.settrace()` per thread.\n\n7. **asyncio.** `pdb` works in coroutines but `await` inside pdb requires Python 3.13+ or `await` from `interact` mode on older versions. For 3.11\u002F3.12, use `asyncio.run_coroutine_threadsafe` tricks or `!stmt`-based awaits via `asyncio.ensure_future`.\n\n8. **`scripts\u002Frun_tests.sh` strips credentials and sets `HOME=\u003Ctmpdir>`.** If your bug depends on user config or real API keys, it won't reproduce under the wrapper. Debug with raw `pytest` first to repro, then re-confirm under the wrapper.\n\n9. **Forking \u002F multiprocessing.** pdb does not follow forks. Each child needs its own `breakpoint()` or `set_trace()`. For Hermes subagents, debug one process at a time.\n\n## Verification Checklist\n\n- [ ] After `pip install debugpy`, confirm: `python -c \"import debugpy; print(debugpy.__version__)\"`\n- [ ] For remote debug, confirm the port is actually listening: `ss -tlnp | grep 5678`\n- [ ] First breakpoint actually hits (if it doesn't, you likely have `PYTHONBREAKPOINT=0`, you're under xdist, or execution finished before attach)\n- [ ] `where` \u002F `w` shows the expected call stack\n- [ ] Post-debug cleanup: no stray `breakpoint()` \u002F `set_trace()` in committed code\n  ```bash\n  rg -n 'breakpoint\\(\\)|set_trace\\(|debugpy\\.listen' --type py\n  ```\n\n## One-Shot Recipes\n\n**\"Why is this dict missing a key?\"**\n```python\n# add above the KeyError site\nbreakpoint()\n# then in pdb:\n(Pdb) pp d\n(Pdb) pp list(d.keys())\n(Pdb) w                # how did we get here\n```\n\n**\"This test passes in isolation but fails in the suite.\"**\n```bash\nscripts\u002Frun_tests.sh tests\u002Fthe_test.py --pdb -p no:xdist\n# But if it only fails WITH other tests:\nsource .venv\u002Fbin\u002Factivate\npython -m pytest tests\u002F -x --pdb -p no:xdist\n# Now it pdb-traps at the exact failing test after state accumulated.\n```\n\n**\"My async handler deadlocks.\"**\n```python\n# Add at handler entry\nimport remote_pdb; remote_pdb.set_trace(host=\"127.0.0.1\", port=4444)\n```\nTrigger the handler. `nc 127.0.0.1 4444`, then `w` to see the suspended frame, `!import asyncio; asyncio.all_tasks()` to see what else is pending.\n\n**\"Post-mortem on a crash in an Ink child process \u002F subprocess.\"**\n```bash\nPYTHONFAULTHANDLER=1 python -m pdb -c continue path\u002Fto\u002Fentrypoint.py\n# On crash, pdb lands at the frame of the exception with full locals\n```\n","# Python Debugger (pdb + debugpy)\n\n## Overview\n\nThree tools, picked by situation:\n\n| Tool | When |\n|---|---|\n| **`breakpoint()` + pdb** | Local, interactive, simplest. Add `breakpoint()` in the source, run normally, get a REPL at that line. |\n| **`python -m pdb`** | Launch an existing script under pdb with no source edits. Useful for quick poking. |\n| **`debugpy`** | Remote \u002F headless \u002F \"attach to already-running process.\" Talks DAP, scriptable from terminal, works for long-lived processes (gateway, daemon, PTY children). |\n\n**Start with `breakpoint()`.** It's the cheapest thing that works.\n\n## When to Use\n\n- A test fails and the traceback doesn't reveal why a value is wrong\n- You need to step through a function and watch a collection mutate\n- A long-running process (hermes gateway, tui_gateway) misbehaves and you can't restart it\n- Post-mortem: an exception fired in prod-ish code and you want to inspect locals at the crash site\n- A subprocess \u002F child (Python `_SlashWorker`, PTY bridge worker) is the actual bug site\n\n**Don't use for:** things `print()` \u002F `logging.debug` solve in under a minute, or things `pytest -vv --tb=long --showlocals` already reveals.\n\n## pdb Quick Reference\n\nInside any pdb prompt (`(Pdb)`):\n\n| Command | Action |\n|---|---|\n| `h` \u002F `h cmd` | help |\n| `n` | next line (step over) |\n| `s` | step into |\n| `r` | return from current function |\n| `c` | continue |\n| `unt N` | continue until line N |\n| `j N` | jump to line N (same function only) |\n| `l` \u002F `ll` | list source around current line \u002F full function |\n| `w` | where (stack trace) |\n| `u` \u002F `d` | move up \u002F down in the stack |\n| `a` | print args of the current function |\n| `p expr` \u002F `pp expr` | print \u002F pretty-print expression |\n| `display expr` | auto-print expr on every stop |\n| `b file:line` | set breakpoint |\n| `b func` | break on function entry |\n| `b file:line, cond` | conditional breakpoint |\n| `cl N` | clear breakpoint N |\n| `tbreak file:line` | one-shot breakpoint |\n| `!stmt` | execute arbitrary Python (assignments included) |\n| `interact` | drop into full Python REPL in current scope (Ctrl+D to exit) |\n| `q` | quit |\n\nThe `interact` command is the most powerful — you can import anything, inspect complex objects, even call methods that mutate state. Locals are read-only by default; use `!x = 42` from the `(Pdb)` prompt to mutate.\n\n## Recipe 1: Local breakpoint\n\nEasiest. Edit the file:\n\n```python\ndef compute(x, y):\n    result = some_helper(x)\n    breakpoint()           # \u003C-- drops into pdb here\n    return result + y\n```\n\nRun the code normally. You land at the `breakpoint()` line with full access to locals.\n\n**Don't forget to remove `breakpoint()` before committing.** Use `git diff` or a pre-commit grep:\n```bash\nrg -n 'breakpoint\\(\\)' --type py\n```\n\n## Recipe 2: Launch a script under pdb (no source edits)\n\n```bash\npython -m pdb path\u002Fto\u002Fscript.py arg1 arg2\n# Lands at first line of script\n(Pdb) b path\u002Fto\u002Fscript.py:42\n(Pdb) c\n```\n\n## Recipe 3: Debug a pytest test\n\nThe hermes test runner and pytest both support this:\n\n```bash\n# Drop to pdb on failure (or on any raised exception):\nscripts\u002Frun_tests.sh tests\u002Fpath\u002Fto\u002Ftest_file.py::test_name --pdb\n\n# Drop to pdb at the START of the test:\nscripts\u002Frun_tests.sh tests\u002Fpath\u002Fto\u002Ftest_file.py::test_name --trace\n\n# Show locals in tracebacks without pdb:\nscripts\u002Frun_tests.sh tests\u002Fpath\u002Fto\u002Ftest_file.py --showlocals --tb=long\n```\n\nNote: `scripts\u002Frun_tests.sh` uses xdist (`-n 4`) by default, and pdb does NOT work under xdist. Add `-p no:xdist` or run a single test with `-n 0`:\n\n```bash\nscripts\u002Frun_tests.sh tests\u002Ffoo_test.py::test_bar --pdb -p no:xdist\n# or\nsource .venv\u002Fbin\u002Factivate\npython -m pytest tests\u002Ffoo_test.py::test_bar --pdb\n```\n\nThis bypasses the hermetic-env guarantees — fine for debugging, but re-run under the wrapper to confirm before pushing.\n\n## Recipe 4: Post-mortem on any exception\n\n```python\nimport pdb, sys\ntry:\n    run_the_thing()\nexcept Exception:\n    pdb.post_mortem(sys.exc_info()[2])\n```\n\nOr wrap a whole script:\n\n```bash\npython -m pdb -c continue script.py\n# When it crashes, pdb catches it and you're in the frame of the exception\n```\n\nOr set a global hook in a repl\u002Fjupyter:\n\n```python\nimport sys\ndef excepthook(etype, value, tb):\n    import pdb; pdb.post_mortem(tb)\nsys.excepthook = excepthook\n```\n\n## Recipe 5: Remote debug with debugpy (attach to running process)\n\nFor long-lived processes: Hermes gateway, tui_gateway, a daemon, a process that's already misbehaving and can't be restarted clean.\n\n### Setup\n\n```bash\nsource \u002Fhome\u002Fbb\u002Fhermes-agent\u002F.venv\u002Fbin\u002Factivate\npip install debugpy\n```\n\n### Pattern A: Source-edit — process waits for debugger at launch\n\nAdd near the top of the entry point (or inside the function you want to debug):\n\n```python\nimport debugpy\ndebugpy.listen((\"127.0.0.1\", 5678))\nprint(\"debugpy listening on 5678, waiting for client...\", flush=True)\ndebugpy.wait_for_client()\ndebugpy.breakpoint()       # optional: pause immediately once attached\n```\n\nStart the process; it blocks on `wait_for_client()`.\n\n### Pattern B: No source edit — launch with `-m debugpy`\n\n```bash\npython -m debugpy --listen 127.0.0.1:5678 --wait-for-client your_script.py arg1\n```\n\nEquivalent for module entry:\n\n```bash\npython -m debugpy --listen 127.0.0.1:5678 --wait-for-client -m your.module\n```\n\n### Pattern C: Attach to an already-running process\n\nNeeds the PID and debugpy preinstalled in the target's environment:\n\n```bash\npython -m debugpy --listen 127.0.0.1:5678 --pid \u003Cpid>\n# debugpy injects itself into the process. Then attach a client as below.\n```\n\nSome kernels\u002Fsecurity configs block the ptrace-based injection (`\u002Fproc\u002Fsys\u002Fkernel\u002Fyama\u002Fptrace_scope`). Fix with:\n```bash\necho 0 | sudo tee \u002Fproc\u002Fsys\u002Fkernel\u002Fyama\u002Fptrace_scope\n```\n\n### Connecting a client from the terminal\n\nThe easiest terminal-side DAP client is VS Code CLI or a small script. From inside Hermes you have two practical options:\n\n**Option 1: `debugpy`'s own CLI REPL** — not an official feature, but a tiny DAP client script:\n\n```python\n# \u002Ftmp\u002Fdap_client.py\nimport socket, json, itertools, time, sys\n\nHOST, PORT = \"127.0.0.1\", 5678\ns = socket.create_connection((HOST, PORT))\nseq = itertools.count(1)\n\ndef send(msg):\n    msg[\"seq\"] = next(seq)\n    body = json.dumps(msg).encode()\n    s.sendall(f\"Content-Length: {len(body)}\\r\\n\\r\\n\".encode() + body)\n\ndef recv():\n    header = b\"\"\n    while b\"\\r\\n\\r\\n\" not in header:\n        header += s.recv(1)\n    length = int(header.decode().split(\"Content-Length:\")[1].split(\"\\r\\n\")[0].strip())\n    body = b\"\"\n    while len(body) \u003C length:\n        body += s.recv(length - len(body))\n    return json.loads(body)\n\nsend({\"type\": \"request\", \"command\": \"initialize\", \"arguments\": {\"adapterID\": \"python\"}})\nprint(recv())\nsend({\"type\": \"request\", \"command\": \"attach\", \"arguments\": {}})\nprint(recv())\nsend({\"type\": \"request\", \"command\": \"setBreakpoints\",\n      \"arguments\": {\"source\": {\"path\": sys.argv[1]},\n                    \"breakpoints\": [{\"line\": int(sys.argv[2])}]}})\nprint(recv())\nsend({\"type\": \"request\", \"command\": \"configurationDone\"})\n# ... loop reading events and sending continue\u002FstepIn\u002Fetc.\n```\n\nThis is fine for one-off automation but painful as an interactive UX.\n\n**Option 2: Attach from VS Code \u002F Cursor \u002F Zed** — if the user has one open, they can add a `launch.json`:\n\n```json\n{\n  \"name\": \"Attach to Hermes\",\n  \"type\": \"debugpy\",\n  \"request\": \"attach\",\n  \"connect\": { \"host\": \"127.0.0.1\", \"port\": 5678 },\n  \"justMyCode\": false,\n  \"pathMappings\": [\n    { \"localRoot\": \"${workspaceFolder}\", \"remoteRoot\": \"\u002Fhome\u002Fbb\u002Fhermes-agent\" }\n  ]\n}\n```\n\n**Option 3: Ditch DAP, use `remote-pdb`** — usually what you actually want from a terminal agent:\n\n```bash\npip install remote-pdb\n```\n\nIn your code:\n```python\nfrom remote_pdb import set_trace\nset_trace(host=\"127.0.0.1\", port=4444)   # blocks until connection\n```\n\nThen from the terminal:\n```bash\nnc 127.0.0.1 4444\n# You get a (Pdb) prompt exactly as if debugging locally.\n```\n\n`remote-pdb` is the cleanest agent-friendly choice when `debugpy`'s DAP protocol is overkill. Use `debugpy` only when you actually need IDE integration.\n\n## Debugging Hermes-specific Processes\n\n### Tests\nSee Recipe 3. Always add `-p no:xdist` or run single tests without xdist.\n\n### `run_agent.py` \u002F CLI — one-shot\nEasiest: add `breakpoint()` near the suspect line, then run `hermes` normally. Control returns to your terminal at the pause point.\n\n### `tui_gateway` subprocess (spawned by `hermes --tui`)\nThe gateway runs as a child of the Node TUI. Options:\n\n**A. Source-edit the gateway:**\n```python\n# tui_gateway\u002Fserver.py near the top of serve()\nimport debugpy\ndebugpy.listen((\"127.0.0.1\", 5678))\ndebugpy.wait_for_client()\n```\nStart `hermes --tui`. The TUI will appear frozen (its backend is waiting). Attach a client; execution resumes when you `continue`.\n\n**B. Use `remote-pdb` at a specific handler:**\n```python\nfrom remote_pdb import set_trace\nset_trace(host=\"127.0.0.1\", port=4444)   # in the RPC handler you want to trap\n```\nTrigger the matching slash command from the TUI, then `nc 127.0.0.1 4444` in another terminal.\n\n### `_SlashWorker` subprocess\nSame pattern — `remote-pdb` with `set_trace()` inside the worker's `exec` path. The worker is persistent across slash commands, so the first trigger blocks until you connect; subsequent slash commands pass through normally unless you re-arm.\n\n### Gateway (`gateway\u002Frun.py`)\nLong-lived. Use `remote-pdb` at a handler, or `debugpy` with `--wait-for-client` if you're restarting the gateway anyway.\n\n## Common Pitfalls\n\n1. **pdb under pytest-xdist silently does nothing.** You won't see the prompt, the test just hangs. Always use `-p no:xdist` or `-n 0`.\n\n2. **`breakpoint()` in CI \u002F non-TTY contexts hangs the process.** Safe locally; never commit it. Add a pre-commit grep as a safety net.\n\n3. **`PYTHONBREAKPOINT=0`** disables all `breakpoint()` calls. Check the env if your breakpoint isn't hitting:\n   ```bash\n   echo $PYTHONBREAKPOINT\n   ```\n\n4. **`debugpy.listen` blocks only if you also call `wait_for_client()`.** Without it, execution continues and your first breakpoint may fire before the client is attached.\n\n5. **Attach to PID fails on hardened kernels.** `ptrace_scope=1` (Ubuntu default) allows only same-user ptrace of child processes. Workaround: `echo 0 > \u002Fproc\u002Fsys\u002Fkernel\u002Fyama\u002Fptrace_scope` (needs root) or launch under `debugpy` from the start.\n\n6. **Threads.** `pdb` only debugs the current thread. For multithreaded code, use `debugpy` (thread-aware DAP) or set `threading.settrace()` per thread.\n\n7. **asyncio.** `pdb` works in coroutines but `await` inside pdb requires Python 3.13+ or `await` from `interact` mode on older versions. For 3.11\u002F3.12, use `asyncio.run_coroutine_threadsafe` tricks or `!stmt`-based awaits via `asyncio.ensure_future`.\n\n8. **`scripts\u002Frun_tests.sh` strips credentials and sets `HOME=\u003Ctmpdir>`.** If your bug depends on user config or real API keys, it won't reproduce under the wrapper. Debug with raw `pytest` first to repro, then re-confirm under the wrapper.\n\n9. **Forking \u002F multiprocessing.** pdb does not follow forks. Each child needs its own `breakpoint()` or `set_trace()`. For Hermes subagents, debug one process at a time.\n\n## Verification Checklist\n\n- [ ] After `pip install debugpy`, confirm: `python -c \"import debugpy; print(debugpy.__version__)\"`\n- [ ] For remote debug, confirm the port is actually listening: `ss -tlnp | grep 5678`\n- [ ] First breakpoint actually hits (if it doesn't, you likely have `PYTHONBREAKPOINT=0`, you're under xdist, or execution finished before attach)\n- [ ] `where` \u002F `w` shows the expected call stack\n- [ ] Post-debug cleanup: no stray `breakpoint()` \u002F `set_trace()` in committed code\n  ```bash\n  rg -n 'breakpoint\\(\\)|set_trace\\(|debugpy\\.listen' --type py\n  ```\n\n## One-Shot Recipes\n\n**\"Why is this dict missing a key?\"**\n```python\n# add above the KeyError site\nbreakpoint()\n# then in pdb:\n(Pdb) pp d\n(Pdb) pp list(d.keys())\n(Pdb) w                # how did we get here\n```\n\n**\"This test passes in isolation but fails in the suite.\"**\n```bash\nscripts\u002Frun_tests.sh tests\u002Fthe_test.py --pdb -p no:xdist\n# But if it only fails WITH other tests:\nsource .venv\u002Fbin\u002Factivate\npython -m pytest tests\u002F -x --pdb -p no:xdist\n# Now it pdb-traps at the exact failing test after state accumulated.\n```\n\n**\"My async handler deadlocks.\"**\n```python\n# Add at handler entry\nimport remote_pdb; remote_pdb.set_trace(host=\"127.0.0.1\", port=4444)\n```\nTrigger the handler. `nc 127.0.0.1 4444`, then `w` to see the suspended frame, `!import asyncio; asyncio.all_tasks()` to see what else is pending.\n\n**\"Post-mortem on a crash in an Ink child process \u002F subprocess.\"**\n```bash\nPYTHONFAULTHANDLER=1 python -m pdb -c continue path\u002Fto\u002Fentrypoint.py\n# On crash, pdb lands at the frame of the exception with full locals\n```\n",{"name":5,"author":49,"license":14,"version":50,"metadata":51,"description":7},"Hermes Agent","1.0.0",{"hermes":52},{"tags":53,"related_skills":58},[31,28,39,54,55,56,57],"debugpy","breakpoints","dap","post-mortem",[43,59,60],"node-inspect-debugger","debugging-hermes-tui-commands","master","8ec37df6d71fb664ea19d32c079e7d3ff931a8e0","skills\u002Fops\u002Fpython-debugpy",3,"https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit","https:\u002F\u002Fweblyfe.ai","https:\u002F\u002Fgithub.com\u002FS3YED","2026-08-27 01:54:02",{"source":8,"skillName":5,"commands":70,"prompt":80,"zipAvailable":81},[71,74,77],{"tool":72,"command":73},"npx","npx skills add S3YED\u002Fappie-kit --skill python-debugpy",{"tool":75,"command":76},"bunx","bunx skills add S3YED\u002Fappie-kit --skill python-debugpy",{"tool":78,"command":79},"pnpm","pnpm dlx skills add S3YED\u002Fappie-kit --skill python-debugpy","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fops\u002Fpython-debugpy\u002FSKILL.md",true,[],[84,114,147,173,204,227],{"skillNo":85,"name":86,"nameZh":6,"description":87,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":88,"categories":89,"keywords":97,"publishedAt":44},"01M6JFD4M6N4DSGC7TXEJ5R3J2","ai-music","AI music generation and audio tools: HeartMuLa lyrics-to-music generation, songwriting craft (+ Suno prompts), and audio spectrogram\u002Ffeature visualization.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fwethlete\u002Fai-music\u002FSKILL.md",[90,93,96],{"categoryNo":91,"categoryName":92,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000013","分析与可视化",{"categoryNo":94,"categoryName":95,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000016","图像与多媒体",{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[98,99,100,101,102,103,33,34,104,105,106,107,108,109,110,111,112,113],"music","ai music","humanizer","visualization","Tools","Audio","songwriting","suno","lyrics","prompts","heartmula","craft","generation","feature","ai music generation","music generation",{"skillNo":115,"name":116,"nameZh":6,"description":117,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":118,"categories":119,"keywords":126,"publishedAt":44},"01M6JFCZE4XNFQFMNZDHKHT5V5","agentic-webdesign-design-system-ingestion","Use v0 Design Systems 2.0, Bolt Design System Agents, or Lovable to generate production UI from a client's real design system components — not generic Tailwind. Use when building a client website where brand consistency matters.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fweb-design\u002Fagentic-webdesign-design-system-ingestion\u002FSKILL.md",[120,123],{"categoryNo":121,"categoryName":122,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":124,"categoryName":125,"taxonomyType":25,"taxonomyTypeDesc":26},"01JBSEEDCAT000000000000032","设计师",[127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146],"design","agentic","design system","system","ingestion","webdesign","agentic webdesign design system ingestion","components","generate","design systems","website","ui","Tailwind","bolt","lovable","v0","design-system","wed","design system components","appie",{"skillNo":148,"name":149,"nameZh":6,"description":150,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":151,"categories":152,"keywords":155,"publishedAt":44},"01M6JFCVPX3913CFAS6QQJ33ZB","agentic-webdesign-agent-elements","Use 21st.dev Agent Elements — 25 shadcn\u002Fui components purpose-built for AI agent UIs. Tool cards (Bash, Edit, Search, Plan, Subagent, MCP), chat, input, streaming. Open source, shadcn-registry install, no runtime lock-in.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fweb-design\u002Fagentic-webdesign-agent-elements\u002FSKILL.md",[153,154],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":124,"categoryName":125,"taxonomyType":25,"taxonomyTypeDesc":26},[128,156,132,157,158,159,134,160,161,162,163,164,165,166,167,168,169,170,171,172],"elements","agentic webdesign agent elements","MCP","ai agent","registry","search","streaming","open source","open","Bash","plan","subagent","edit","install","input","lock","source",{"skillNo":174,"name":175,"nameZh":6,"description":176,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":177,"categories":178,"keywords":183,"publishedAt":44},"01M6JFCQC7H65M9QJ6TRE71BS1","vercel-deployment","Deploy static sites and frontends to Vercel — handles both CLI and REST API paths, including the CLI token sanitization workaround.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fsoftware-development\u002Fvercel-deployment\u002FSKILL.md",[179,182],{"categoryNo":180,"categoryName":181,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000011","运维与部署",{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203],"deployment","vercel deployment","vercel","macos","cli","linux","windows","deploy","vercel deploy","REST","api","site","deploy to vercel","token","rest api","host","sites","paths","sanitization","static site",{"skillNo":205,"name":206,"nameZh":6,"description":207,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":208,"categories":209,"keywords":211,"publishedAt":44},"01M6JFCM4SMTXBM5A01BK2YQG1","software-quality-workflows","Umbrella workflow for TDD, debugging, code simplification, spikes, pre-commit review, and debugger-assisted investigation.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fsoftware-development\u002Fsoftware-quality-workflows\u002FSKILL.md",[210],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[212,213,214,215,216,217,218,219,31,220,32,221,222,223,224,225,226],"workflows","quality","software","software quality workflows","review","tdd","workflow","commit","code","pre-commit","investigation","code simplification","user","simplification","for",{"skillNo":228,"name":229,"nameZh":6,"description":230,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":231,"categories":232,"keywords":240,"publishedAt":44},"01M6JFCGD0M6CEATY01W7YN6DB","react-component-refactoring","Safely refactor large React components into smaller, maintainable pieces using proven patterns","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fsoftware-development\u002Freact-component-refactoring\u002FSKILL.md",[233,236,239],{"categoryNo":234,"categoryName":235,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000007","代码审查",{"categoryNo":237,"categoryName":238,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发",{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[241,242,243,244,245,246,247,248,187,249,250,134,251,190,252,220,253,254,255,256],"react","component","refactoring","component refactoring","react component","react component refactoring","frontend","writing","safe","typescript","react components","modular","refactor","simplify","patterns","test-driven-development"]