textual-debugger 0.1.4__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/PKG-INFO +157 -21
  2. {textual_debugger-0.1.4/src/tdb → textual_debugger-0.2.0}/README.md +156 -20
  3. {textual_debugger-0.1.4 → textual_debugger-0.2.0/src/tdb}/README.md +156 -20
  4. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/__init__.py +1 -1
  5. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/app.py +63 -13
  6. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/app_handlers/dap_events.py +2 -0
  7. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/app_handlers/inspection.py +56 -8
  8. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/cli.py +61 -2
  9. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/dap/client.py +45 -75
  10. textual_debugger-0.2.0/src/tdb/languages/__init__.py +21 -0
  11. textual_debugger-0.2.0/src/tdb/languages/base.py +139 -0
  12. textual_debugger-0.2.0/src/tdb/languages/cpp.py +150 -0
  13. textual_debugger-0.2.0/src/tdb/languages/python.py +116 -0
  14. textual_debugger-0.2.0/src/tdb/languages/registry.py +106 -0
  15. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/mcp/server.py +12 -2
  16. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/mcp/session.py +48 -4
  17. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/persist.py +15 -1
  18. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/server/handlers.py +9 -4
  19. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/server/runner.py +23 -9
  20. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/session/child_processes.py +7 -0
  21. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/session/controller.py +71 -19
  22. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/session/inspect_service.py +14 -2
  23. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/session/statement_stepper.py +16 -3
  24. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/source_analysis.py +20 -4
  25. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/code_view.py +6 -3
  26. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/textual_debugger.egg-info/PKG-INFO +157 -21
  27. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/textual_debugger.egg-info/SOURCES.txt +5 -0
  28. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/LICENSE +0 -0
  29. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/pyproject.toml +0 -0
  30. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/setup.cfg +0 -0
  31. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/__main__.py +0 -0
  32. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/_timeouts.py +0 -0
  33. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/app_handlers/__init__.py +0 -0
  34. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/app_handlers/routing.py +0 -0
  35. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/app_handlers/ui_panels.py +0 -0
  36. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/app_helpers.py +0 -0
  37. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/breakpoint_hook.py +0 -0
  38. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/dap/__init__.py +0 -0
  39. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/dap/messages.py +0 -0
  40. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/dap/protocol.py +0 -0
  41. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/dap/types.py +0 -0
  42. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/inspection.py +0 -0
  43. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/inspection_full.py +0 -0
  44. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/keybindings/__init__.py +0 -0
  45. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/mcp/__init__.py +0 -0
  46. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/mcp/__main__.py +0 -0
  47. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/post_mortem.py +0 -0
  48. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/processes_cache.py +0 -0
  49. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/server/__init__.py +0 -0
  50. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/server/app.py +0 -0
  51. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/server/event_handler.py +0 -0
  52. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/server/rpc_types.py +0 -0
  53. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/session/__init__.py +0 -0
  54. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/session/event_bus.py +0 -0
  55. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/session/messages.py +0 -0
  56. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/session/post_mortem_loader.py +0 -0
  57. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/session/state.py +0 -0
  58. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/session/terminal.py +0 -0
  59. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/session/textual_handler.py +0 -0
  60. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/__init__.py +0 -0
  61. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/_inspection_modal.py +0 -0
  62. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/async_tasks_modal.py +0 -0
  63. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/breakpoint_view.py +0 -0
  64. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/console_view.py +0 -0
  65. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/evaluate_console.py +0 -0
  66. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/full_contents_modal.py +0 -0
  67. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/menu_bar.py +0 -0
  68. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/modals.py +0 -0
  69. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/processes_modal.py +0 -0
  70. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/stack_view.py +0 -0
  71. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/status_bar.py +0 -0
  72. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/threads_modal.py +0 -0
  73. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/tdb/widgets/variable_view.py +0 -0
  74. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/textual_debugger.egg-info/dependency_links.txt +0 -0
  75. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/textual_debugger.egg-info/entry_points.txt +0 -0
  76. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/textual_debugger.egg-info/requires.txt +0 -0
  77. {textual_debugger-0.1.4 → textual_debugger-0.2.0}/src/textual_debugger.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: textual-debugger
3
- Version: 0.1.4
3
+ Version: 0.2.0
4
4
  Summary: A TUI Python debugger based on textual and debugpy
5
5
  Author-email: Al Danial <al.danial@gmail.com>
6
6
  License: textual-debugger is licensed to you under the MIT/X Consortium license:
@@ -67,12 +67,18 @@ Dynamic: license-file
67
67
  # `textual-debugger`
68
68
 
69
69
  `textual-debugger` (the package) provides `tdb` (the command-line tool and module),
70
- a full-featured terminal-based Python debugger.
71
-
72
- `tdb` is built with [textual](https://github.com/Textualize/textual)
73
- and [debugpy](https://github.com/microsoft/debugpy) (the Debug Adapter Protocol engine behind
74
- VS Code's Python debugger). It provides a rich interactive interface for stepping through code,
75
- inspecting variables, managing breakpoints, and evaluating expressions in complex Python programs.
70
+ a full-featured terminal-based debugger for Python and other languages
71
+ with a Debug Adapter Protocol (DAP) implementation. C and C++ support (via
72
+ `gdb` or `lldb-dap`) is built in.
73
+
74
+ `tdb` is built with [textual](https://github.com/Textualize/textual) and speaks
75
+ DAP to a pluggable debug adapter: [debugpy](https://github.com/microsoft/debugpy)
76
+ (the engine behind VS Code's Python debugger) for Python,
77
+ [GDB's DAP mode](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Debugger-Adapter-Protocol.html) or
78
+ [lldb-dap](https://lldb.llvm.org/resources/lldbdap.html)
79
+ for compiled code. It provides a rich interactive interface for stepping through
80
+ code, inspecting variables, managing breakpoints, and evaluating expressions in
81
+ complex programs.
76
82
 
77
83
  - PyPI: https://pypi.org/project/textual-debugger/
78
84
  - GitHub: https://github.com/AlDanial/tdb
@@ -83,6 +89,10 @@ MIT License. Copyright 2026 by Al Danial.
83
89
 
84
90
  `tdb`:
85
91
 
92
+ - debugs multiple languages through the Debug Adapter Protocol: Python (via `debugpy`,
93
+ the richest feature set) and C/C++ (via `gdb -i dap` or `lldb-dap`), with the language
94
+ auto-detected from the target (ref. [Multi-Language Debugging](#multi-language-debugging)).
95
+
86
96
  - supports debugging of synchronous, asynchronous, multi-threaded, and multi-process Python code.
87
97
  It specifically supports modules
88
98
  - `asyncio` (with a built-in async task inspector and task wait graph)
@@ -91,7 +101,8 @@ It specifically supports modules
91
101
 
92
102
  - supports remote attachment to debugpy-enabled Python programs
93
103
 
94
- - includes a JSON-RPC server mode that enables programmatic debug control, making it suitable for
104
+ - includes a JSON-RPC server mode, an MCP mode, and a `SKILL.md` file that enable
105
+ programmatic debug control, making it suitable for
95
106
  automated, headless debugging workflows and AI-assisted debugging
96
107
 
97
108
  - can spawn the debuggee in an external terminal to enable debugging TUI applications
@@ -120,6 +131,9 @@ as open source.
120
131
  [Claude for Open Source](https://claude.com/contact-sales/claude-for-oss) program.
121
132
  `tdb` was made almost entirely with Claude Code.
122
133
 
134
+ - OpenAI, for providing access to Codex through the
135
+ [Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
136
+
123
137
  ## Gallery
124
138
  <p align="center">
125
139
  <img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_breakpoint.png" alt="at breakpoint" width="300">
@@ -165,6 +179,16 @@ tdb my_program.py
165
179
  # debug with arguments
166
180
  tdb my_program.py arg1 arg2
167
181
 
182
+ # debug a C/C++ (or other native) executable built with -g. The ELF/Mach-O/PE
183
+ # binary is auto-detected and debugged through GDB's DAP mode (GDB >= 14)
184
+ tdb ./myprog arg1 arg2
185
+
186
+ # same, but using lldb-dap (LLVM >= 17) instead of gdb
187
+ tdb --adapter lldb-dap ./myprog
188
+
189
+ # force the language when auto-detection can't tell (e.g. an extensionless script)
190
+ tdb --lang python ./mytool
191
+
168
192
  # add breakpoints at lines 20 and 35 of `my_program.py` and line 14
169
193
  # of `module.py` (when -k is given, --no-stop-on-entry is set and the
170
194
  # program runs to the first breakpoint)
@@ -206,6 +230,79 @@ Alternatively, use the module entry point:
206
230
  python -m tdb my_program.py
207
231
  ```
208
232
 
233
+ ## Multi-Language Debugging
234
+
235
+ `tdb` debugs any language that has a Debug Adapter Protocol backend. Two
236
+ languages are supported out of the box:
237
+
238
+ | Language | Adapter(s) | How to get the adapter | Feature level |
239
+ |----------|------------|------------------------|---------------|
240
+ | Python | `debugpy` (default) | installed with `textual-debugger` | everything in this README |
241
+ | C / C++ (any native binary) | `gdb` (default), `lldb-dap` (alternate) | `gdb -i dap` requires GDB ≥ 14; `lldb-dap` ships with LLVM ≥ 17 (e.g. `apt install lldb`) | core debugging: breakpoints, stepping, stack, variables, evaluate console |
242
+
243
+ ### Language detection and selection
244
+
245
+ The language is auto-detected from the debug target:
246
+
247
+ 1. File extension: `.py` → Python.
248
+ 2. Native executables (ELF, Mach-O, PE magic bytes) → C/C++.
249
+ 3. A `#!...python` shebang → Python.
250
+ 4. C/C++/Rust *source* files (`.c`, `.cpp`, `.rs`, …) produce an error with a
251
+ hint: compile with debug info (`g++ -g -O0`) and debug the binary.
252
+ 5. Anything else produces an error naming the `--lang` override.
253
+
254
+ `--lang` forces the language; `--adapter` picks a non-default adapter within
255
+ it (`tdb --lang cpp --adapter lldb-dap ./myprog`).
256
+
257
+ > **Migration note:** extensionless Python scripts without a `python` shebang
258
+ > were previously assumed to be Python; they now require `--lang python`.
259
+
260
+ ### Adapters are found on `PATH`
261
+
262
+ `tdb` does not download or bundle adapters. If the adapter executable isn't
263
+ found, the error names the package to install. To use an adapter from a
264
+ non-standard location, or change a language's default adapter, add to
265
+ `config.json` (see [Configuration](#configuration)):
266
+
267
+ ```json
268
+ {
269
+ "adapters": {"lldb-dap": "/opt/llvm/bin/lldb-dap"},
270
+ "default_adapters": {"cpp": "lldb-dap"}
271
+ }
272
+ ```
273
+
274
+ ### What works for non-Python languages
275
+
276
+ Core debugging works identically for every language: breakpoints (incl.
277
+ conditions and persistence), stepping, continue/pause, run-to-cursor, stack
278
+ navigation, variable inspection, the evaluate console, syntax highlighting,
279
+ and the JSON-RPC / MCP programmatic modes.
280
+
281
+ Python-specific features are hidden or return a clear "not supported for this
282
+ language" message when debugging other languages: statement-granularity
283
+ stepping (non-Python languages always step per line), the async task /
284
+ process inspectors and wait graph, the evaluate console's trailing-`?` help,
285
+ remote attach (`-r`), `--python`/`--pv`, `--no-subprocess`,
286
+ automatic child-process attachment, and the post-mortem / `tdb.breakpoint()`
287
+ hooks (those hooks live inside Python programs by nature). `--terminal` is
288
+ currently ignored for non-Python targets.
289
+
290
+ ### C/C++ tips
291
+
292
+ - Compile with `-g` (ideally `-g -O0`). If no breakpoint in a file can be
293
+ bound, `tdb` prints a console warning suggesting the program may lack
294
+ debug info.
295
+ - Stack frames pointing into system libraries often have no source on disk;
296
+ the Code View shows a `<Could not read …>` placeholder while the stack,
297
+ variables, and evaluate console remain fully usable.
298
+ - GDB (the default adapter) has the most complete libstdc++
299
+ pretty-printing. `lldb-dap` (via `--adapter lldb-dap`) also debugs
300
+ GCC-built binaries fine — DWARF is compiler-neutral.
301
+ - **GDB evaluate-console quirk:** GDB's DAP treats REPL input as CLI
302
+ commands, so evaluate expressions with an explicit `print`, e.g.
303
+ `print x` rather than bare `x` (bare `x` collides with GDB's
304
+ examine-memory command). `lldb-dap` evaluates bare expressions directly.
305
+
209
306
  ## Layout
210
307
 
211
308
  ```
@@ -234,7 +331,7 @@ The footer shows the most relevant keybindings for the current mode.
234
331
  ### Navigation and Keybindings
235
332
 
236
333
 
237
- The Code View shows syntax-highlighted Python source with line numbers.
334
+ The Code View shows syntax-highlighted source (lexer chosen per language) with line numbers.
238
335
  A cursor line (blue) tracks your position; the current execution line is highlighted in gold.
239
336
 
240
337
  **View focus shortcuts (global):**
@@ -326,6 +423,9 @@ lands on `print(results)`, not on each interior sub-line of the `gather` call. S
326
423
  stops on each physical line--useful for inspecting how a complex expression is built up.
327
424
  The choice is saved to `~/.config/tdb/config.json`.
328
425
 
426
+ Statement mode requires a source-language model and is currently Python-only;
427
+ other languages always step per line (the Step Mode menu says so if you try).
428
+
329
429
  ### Breakpoints
330
430
 
331
431
  Click the gutter in the Code View to toggle a breakpoint, or press `b` in Debug mode.
@@ -383,6 +483,15 @@ interactive evaluation of expressions in the current scope:
383
483
 
384
484
  Variable values set here are reflected in the running code.
385
485
 
486
+ ### Cut / Paste
487
+
488
+ Expression for the Evaluate Console are often copied from the Code View.
489
+ Doing this in `tdb` differs from traditional terminal behavior, because `textual` applications
490
+ capture mouse events for their own use.
491
+
492
+ Instead, hold the `Shift` key while performing your conventional cut/paste keystrokes or mouse
493
+ operation to get the expected behavior.
494
+
386
495
  ### Console Output
387
496
 
388
497
  The Console View captures stdout (normal text) and stderr (red text) from the debuggee
@@ -442,6 +551,17 @@ Snapshot depth / breadth is capped (5 levels, 50 children per container) to keep
442
551
  capture cheap even for pathological object graphs; cycles are handled via identity
443
552
  memoization.
444
553
 
554
+ ### Post-Mortem within a Docker Container
555
+
556
+ The `textual-debugger` GitHub repository's `examples/` directory has three files
557
+ that show how to run a `tdb`-enabled Python program under `tmux` in a Docker
558
+ container so that you can attach to the container and inspect the program
559
+ in `tdb` post-mortem analysis mode if the program hits an unhandled exception:
560
+
561
+ - [post_mortem_example.py](https://github.com/AlDanial/tdb/blob/main/examples/post_mortem_example.py)
562
+ - [post_mortem_entrypoint.sh](https://github.com/AlDanial/tdb/blob/main/examples/post_mortem_entrypoint.sh)
563
+ - [Dockerfile.post_mortem](https://github.com/AlDanial/tdb/blob/main/examples/Dockerfile.post_mortem)
564
+
445
565
  ### Live Breakpoint Hook
446
566
 
447
567
  `tdb` has an improved implemenation of the standard `breakpoint()` function (or equivalently,
@@ -739,7 +859,7 @@ curl -s -X POST http://127.0.0.1:8150/rpc \
739
859
  | `set_breakpoint` | `["file:line"]` or `["file:line", "condition", "hit_condition"]` | Set a breakpoint |
740
860
  | `remove_breakpoint` | `["file:line"]` | Remove a breakpoint |
741
861
  | `list_breakpoints` | `[]` | Show all breakpoints |
742
- | `continue` | `[]` or `[timeout_s]` | Resume execution; on timeout returns `"still runningcall pause or wait again"` (success) |
862
+ | `continue` | `[]` or `[timeout_s]` | Resume execution; on timeout returns `"still running--call pause or wait again"` (success) |
743
863
  | `next` | `[]` or `[timeout_s]` | Step over |
744
864
  | `step_in` | `[]` or `[timeout_s]` | Step into |
745
865
  | `step_out` | `[]` or `[timeout_s]` | Step out |
@@ -778,7 +898,7 @@ Each is JSON with `event`, `data`, and `timestamp` fields.
778
898
  tdb ships a Model Context Protocol (MCP) server (`tdb-mcp`) that exposes
779
899
  the debugger as a curated set of tools an AI agent can call. The MCP
780
900
  server is a third in-process consumer of the same dispatch handlers the
781
- TUI and the HTTP server use so an agent gets the same lock semantics,
901
+ TUI and the HTTP server use so an agent gets the same lock semantics,
782
902
  including the pause-during-continue bypass, and the same DAP-backed
783
903
  inspection surface.
784
904
 
@@ -817,17 +937,24 @@ client expects to launch servers.
817
937
  | Breakpoints | `set_breakpoint(spec, condition?, hit_condition?)`, `remove_breakpoint(spec)`, `list_breakpoints()` |
818
938
  | Differentiators | `threads(thread_id?)`, `tasks(task_name?)`, `processes(name_or_pid?)`, `wait_graph()` |
819
939
 
820
- `control` is intentionally one tool that takes an action enum the six
940
+ `control` is intentionally one tool that takes an action enum. The six
821
941
  underlying RPC actions share a return shape, and agents perform
822
942
  measurably better with a small surface than with one tool per action.
823
943
  `threads` / `tasks` / `processes` overload list-vs-inspect via a
824
944
  single optional argument for the same reason.
825
945
 
946
+ `debug_launch` accepts optional `lang` and `adapter` parameters mirroring the
947
+ CLI's `--lang`/`--adapter`; when omitted, the language is auto-detected from
948
+ `program`, so an agent can hand it a compiled binary directly. The
949
+ `tasks`/`processes`/`wait_graph` tools stay registered for every language but
950
+ return a structured "not supported when debugging C/C++"-style error for
951
+ non-Python debuggees.
952
+
826
953
  ### Agent flow for a long-running step
827
954
 
828
955
  ```
829
956
  agent → control(action="continue", timeout_s=30)
830
- mcp → "still running call pause or wait again"
957
+ mcp → "still running, call pause or wait again"
831
958
  agent → control(action="pause") # OR: control(action="wait_for_stop", timeout_s=30)
832
959
  mcp → "<file>:<line>"
833
960
  agent → inspect(["x", "len(buf)"])
@@ -836,7 +963,7 @@ mcp → "x = 7\nlen(buf) = 1024"
836
963
 
837
964
  `pause` bypasses the dispatch lock so it can interrupt a `continue`
838
965
  that's still mid-flight (HTTP and MCP share the same `NO_LOCK_ACTIONS`
839
- policy see `tdb/server/app.py`).
966
+ policy; see `tdb/server/app.py`).
840
967
 
841
968
  ### Security caveat
842
969
 
@@ -845,22 +972,23 @@ execution in the debuggee process**. This is inherent to a debugger and
845
972
  not a tdb-specific concern, but MCP clients (and the humans running
846
973
  them) should apply appropriate permission models: don't auto-approve
847
974
  `inspect` against untrusted expressions, and don't expose `tdb-mcp` on
848
- a network stdio transport only by design.
975
+ a network (stdio transport only by design).
849
976
 
850
977
  ### Deferred / out of scope (v1)
851
978
 
852
- - SSE-style event push `control` and `wait_for_stop` make polling
979
+ - SSE-style event push: `control` and `wait_for_stop` make polling
853
980
  efficient enough; events would also need uneven MCP-client support.
854
- - HTTP / streamable-HTTP transports would require auth (which the
981
+ - HTTP / streamable-HTTP transports: would require auth (which the
855
982
  HTTP RPC server also currently lacks); stdio inherits the trust of
856
983
  the process that spawned it.
857
- - Multi-session one debug session per MCP process.
984
+ - Multi-session: one debug session per MCP process.
858
985
 
859
986
  ## CLI Reference
860
987
 
861
988
  ```
862
989
  usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
863
990
  [--no-just-my-code] [--no-subprocess] [--python PYTHON] [--pv]
991
+ [--lang LANGUAGE] [--adapter ADAPTER]
864
992
  [--keybindings {default,vim,emacs}]
865
993
  [--terminal {xterm,konsole,gnome-terminal,ghostty,kitty,iterm2,warp,wezterm,terminator}]
866
994
  [--local-root PATH] [--remote-root PATH]
@@ -876,8 +1004,10 @@ usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
876
1004
  | `-k`, `--breakpoint FILE:LINE|LINE` | Set a breakpoint (may be repeated). Passing `-k` implies `--no-stop-on-entry` so the program runs straight to the first breakpoint. |
877
1005
  | `--no-stop-on-entry` | Do not pause at the first line (default: stop on entry; automatic when `-k` is given) |
878
1006
  | `--cwd DIR` | Working directory for the debuggee |
879
- | `--python PATH` | Python interpreter for the debuggee |
1007
+ | `--python PATH` | Python interpreter for the debuggee (Python targets only) |
880
1008
  | `--pv` | Shorthand for --python .venv/bin/python |
1009
+ | `--lang LANGUAGE` | Debuggee language (`python`, `cpp`); default: auto-detect from the target |
1010
+ | `--adapter ADAPTER` | Debug adapter within the language (e.g. `--lang cpp --adapter lldb-dap`); default: the language's standard adapter |
881
1011
  | `--no-just-my-code` | Step into stdlib/site-packages code instead of skipping it
882
1012
  (default: skipped). On uncaught exceptions, the crash modal always shows the full traceback
883
1013
  including library frames, regardless of this flag. |
@@ -897,9 +1027,14 @@ On Windows, it uses `%APPDATA%\tdb\`.
897
1027
 
898
1028
  | File | Contents |
899
1029
  |------|----------|
900
- | `config.json` | User preferences (keybinding scheme, color theme, step mode) |
1030
+ | `config.json` | User preferences (keybinding scheme, color theme, step mode, adapter overrides) |
901
1031
  | `breakpoints.json` | Breakpoints from previous sessions, keyed by project directory |
902
1032
 
1033
+ Adapter-related keys in `config.json`: `adapters` maps an adapter id to an
1034
+ executable path (`{"adapters": {"lldb-dap": "/opt/llvm/bin/lldb-dap"}}`), and
1035
+ `default_adapters` picks a language's default adapter
1036
+ (`{"default_adapters": {"cpp": "lldb-dap"}}`).
1037
+
903
1038
  Breakpoints are saved on exit and restored when debugging a program in the same
904
1039
  directory. Each project's breakpoints are independent.
905
1040
 
@@ -919,7 +1054,8 @@ fires as expected.
919
1054
  ## Tech Stack
920
1055
 
921
1056
  - [textual](https://github.com/Textualize/textual) : TUI framework
922
- - [debugpy](https://github.com/microsoft/debugpy) : Debug Adapter Protocol implementation
1057
+ - [debugpy](https://github.com/microsoft/debugpy) : Debug Adapter Protocol implementation for Python
1058
+ - [gdb](https://sourceware.org/gdb/) / [lldb-dap](https://lldb.llvm.org/resources/lldbdap.html) : optional, user-installed DAP adapters for C/C++
923
1059
  - [pygments](https://pygments.org/) : Syntax highlighting
924
1060
  - [FastAPI](https://fastapi.tiangolo.com/) + [uvicorn](https://www.uvicorn.org/) : JSON-RPC server
925
1061
 
@@ -1,12 +1,18 @@
1
1
  # `textual-debugger`
2
2
 
3
3
  `textual-debugger` (the package) provides `tdb` (the command-line tool and module),
4
- a full-featured terminal-based Python debugger.
5
-
6
- `tdb` is built with [textual](https://github.com/Textualize/textual)
7
- and [debugpy](https://github.com/microsoft/debugpy) (the Debug Adapter Protocol engine behind
8
- VS Code's Python debugger). It provides a rich interactive interface for stepping through code,
9
- inspecting variables, managing breakpoints, and evaluating expressions in complex Python programs.
4
+ a full-featured terminal-based debugger for Python and other languages
5
+ with a Debug Adapter Protocol (DAP) implementation. C and C++ support (via
6
+ `gdb` or `lldb-dap`) is built in.
7
+
8
+ `tdb` is built with [textual](https://github.com/Textualize/textual) and speaks
9
+ DAP to a pluggable debug adapter: [debugpy](https://github.com/microsoft/debugpy)
10
+ (the engine behind VS Code's Python debugger) for Python,
11
+ [GDB's DAP mode](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Debugger-Adapter-Protocol.html) or
12
+ [lldb-dap](https://lldb.llvm.org/resources/lldbdap.html)
13
+ for compiled code. It provides a rich interactive interface for stepping through
14
+ code, inspecting variables, managing breakpoints, and evaluating expressions in
15
+ complex programs.
10
16
 
11
17
  - PyPI: https://pypi.org/project/textual-debugger/
12
18
  - GitHub: https://github.com/AlDanial/tdb
@@ -17,6 +23,10 @@ MIT License. Copyright 2026 by Al Danial.
17
23
 
18
24
  `tdb`:
19
25
 
26
+ - debugs multiple languages through the Debug Adapter Protocol: Python (via `debugpy`,
27
+ the richest feature set) and C/C++ (via `gdb -i dap` or `lldb-dap`), with the language
28
+ auto-detected from the target (ref. [Multi-Language Debugging](#multi-language-debugging)).
29
+
20
30
  - supports debugging of synchronous, asynchronous, multi-threaded, and multi-process Python code.
21
31
  It specifically supports modules
22
32
  - `asyncio` (with a built-in async task inspector and task wait graph)
@@ -25,7 +35,8 @@ It specifically supports modules
25
35
 
26
36
  - supports remote attachment to debugpy-enabled Python programs
27
37
 
28
- - includes a JSON-RPC server mode that enables programmatic debug control, making it suitable for
38
+ - includes a JSON-RPC server mode, an MCP mode, and a `SKILL.md` file that enable
39
+ programmatic debug control, making it suitable for
29
40
  automated, headless debugging workflows and AI-assisted debugging
30
41
 
31
42
  - can spawn the debuggee in an external terminal to enable debugging TUI applications
@@ -54,6 +65,9 @@ as open source.
54
65
  [Claude for Open Source](https://claude.com/contact-sales/claude-for-oss) program.
55
66
  `tdb` was made almost entirely with Claude Code.
56
67
 
68
+ - OpenAI, for providing access to Codex through the
69
+ [Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
70
+
57
71
  ## Gallery
58
72
  <p align="center">
59
73
  <img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_breakpoint.png" alt="at breakpoint" width="300">
@@ -99,6 +113,16 @@ tdb my_program.py
99
113
  # debug with arguments
100
114
  tdb my_program.py arg1 arg2
101
115
 
116
+ # debug a C/C++ (or other native) executable built with -g. The ELF/Mach-O/PE
117
+ # binary is auto-detected and debugged through GDB's DAP mode (GDB >= 14)
118
+ tdb ./myprog arg1 arg2
119
+
120
+ # same, but using lldb-dap (LLVM >= 17) instead of gdb
121
+ tdb --adapter lldb-dap ./myprog
122
+
123
+ # force the language when auto-detection can't tell (e.g. an extensionless script)
124
+ tdb --lang python ./mytool
125
+
102
126
  # add breakpoints at lines 20 and 35 of `my_program.py` and line 14
103
127
  # of `module.py` (when -k is given, --no-stop-on-entry is set and the
104
128
  # program runs to the first breakpoint)
@@ -140,6 +164,79 @@ Alternatively, use the module entry point:
140
164
  python -m tdb my_program.py
141
165
  ```
142
166
 
167
+ ## Multi-Language Debugging
168
+
169
+ `tdb` debugs any language that has a Debug Adapter Protocol backend. Two
170
+ languages are supported out of the box:
171
+
172
+ | Language | Adapter(s) | How to get the adapter | Feature level |
173
+ |----------|------------|------------------------|---------------|
174
+ | Python | `debugpy` (default) | installed with `textual-debugger` | everything in this README |
175
+ | C / C++ (any native binary) | `gdb` (default), `lldb-dap` (alternate) | `gdb -i dap` requires GDB ≥ 14; `lldb-dap` ships with LLVM ≥ 17 (e.g. `apt install lldb`) | core debugging: breakpoints, stepping, stack, variables, evaluate console |
176
+
177
+ ### Language detection and selection
178
+
179
+ The language is auto-detected from the debug target:
180
+
181
+ 1. File extension: `.py` → Python.
182
+ 2. Native executables (ELF, Mach-O, PE magic bytes) → C/C++.
183
+ 3. A `#!...python` shebang → Python.
184
+ 4. C/C++/Rust *source* files (`.c`, `.cpp`, `.rs`, …) produce an error with a
185
+ hint: compile with debug info (`g++ -g -O0`) and debug the binary.
186
+ 5. Anything else produces an error naming the `--lang` override.
187
+
188
+ `--lang` forces the language; `--adapter` picks a non-default adapter within
189
+ it (`tdb --lang cpp --adapter lldb-dap ./myprog`).
190
+
191
+ > **Migration note:** extensionless Python scripts without a `python` shebang
192
+ > were previously assumed to be Python; they now require `--lang python`.
193
+
194
+ ### Adapters are found on `PATH`
195
+
196
+ `tdb` does not download or bundle adapters. If the adapter executable isn't
197
+ found, the error names the package to install. To use an adapter from a
198
+ non-standard location, or change a language's default adapter, add to
199
+ `config.json` (see [Configuration](#configuration)):
200
+
201
+ ```json
202
+ {
203
+ "adapters": {"lldb-dap": "/opt/llvm/bin/lldb-dap"},
204
+ "default_adapters": {"cpp": "lldb-dap"}
205
+ }
206
+ ```
207
+
208
+ ### What works for non-Python languages
209
+
210
+ Core debugging works identically for every language: breakpoints (incl.
211
+ conditions and persistence), stepping, continue/pause, run-to-cursor, stack
212
+ navigation, variable inspection, the evaluate console, syntax highlighting,
213
+ and the JSON-RPC / MCP programmatic modes.
214
+
215
+ Python-specific features are hidden or return a clear "not supported for this
216
+ language" message when debugging other languages: statement-granularity
217
+ stepping (non-Python languages always step per line), the async task /
218
+ process inspectors and wait graph, the evaluate console's trailing-`?` help,
219
+ remote attach (`-r`), `--python`/`--pv`, `--no-subprocess`,
220
+ automatic child-process attachment, and the post-mortem / `tdb.breakpoint()`
221
+ hooks (those hooks live inside Python programs by nature). `--terminal` is
222
+ currently ignored for non-Python targets.
223
+
224
+ ### C/C++ tips
225
+
226
+ - Compile with `-g` (ideally `-g -O0`). If no breakpoint in a file can be
227
+ bound, `tdb` prints a console warning suggesting the program may lack
228
+ debug info.
229
+ - Stack frames pointing into system libraries often have no source on disk;
230
+ the Code View shows a `<Could not read …>` placeholder while the stack,
231
+ variables, and evaluate console remain fully usable.
232
+ - GDB (the default adapter) has the most complete libstdc++
233
+ pretty-printing. `lldb-dap` (via `--adapter lldb-dap`) also debugs
234
+ GCC-built binaries fine — DWARF is compiler-neutral.
235
+ - **GDB evaluate-console quirk:** GDB's DAP treats REPL input as CLI
236
+ commands, so evaluate expressions with an explicit `print`, e.g.
237
+ `print x` rather than bare `x` (bare `x` collides with GDB's
238
+ examine-memory command). `lldb-dap` evaluates bare expressions directly.
239
+
143
240
  ## Layout
144
241
 
145
242
  ```
@@ -168,7 +265,7 @@ The footer shows the most relevant keybindings for the current mode.
168
265
  ### Navigation and Keybindings
169
266
 
170
267
 
171
- The Code View shows syntax-highlighted Python source with line numbers.
268
+ The Code View shows syntax-highlighted source (lexer chosen per language) with line numbers.
172
269
  A cursor line (blue) tracks your position; the current execution line is highlighted in gold.
173
270
 
174
271
  **View focus shortcuts (global):**
@@ -260,6 +357,9 @@ lands on `print(results)`, not on each interior sub-line of the `gather` call. S
260
357
  stops on each physical line--useful for inspecting how a complex expression is built up.
261
358
  The choice is saved to `~/.config/tdb/config.json`.
262
359
 
360
+ Statement mode requires a source-language model and is currently Python-only;
361
+ other languages always step per line (the Step Mode menu says so if you try).
362
+
263
363
  ### Breakpoints
264
364
 
265
365
  Click the gutter in the Code View to toggle a breakpoint, or press `b` in Debug mode.
@@ -317,6 +417,15 @@ interactive evaluation of expressions in the current scope:
317
417
 
318
418
  Variable values set here are reflected in the running code.
319
419
 
420
+ ### Cut / Paste
421
+
422
+ Expression for the Evaluate Console are often copied from the Code View.
423
+ Doing this in `tdb` differs from traditional terminal behavior, because `textual` applications
424
+ capture mouse events for their own use.
425
+
426
+ Instead, hold the `Shift` key while performing your conventional cut/paste keystrokes or mouse
427
+ operation to get the expected behavior.
428
+
320
429
  ### Console Output
321
430
 
322
431
  The Console View captures stdout (normal text) and stderr (red text) from the debuggee
@@ -376,6 +485,17 @@ Snapshot depth / breadth is capped (5 levels, 50 children per container) to keep
376
485
  capture cheap even for pathological object graphs; cycles are handled via identity
377
486
  memoization.
378
487
 
488
+ ### Post-Mortem within a Docker Container
489
+
490
+ The `textual-debugger` GitHub repository's `examples/` directory has three files
491
+ that show how to run a `tdb`-enabled Python program under `tmux` in a Docker
492
+ container so that you can attach to the container and inspect the program
493
+ in `tdb` post-mortem analysis mode if the program hits an unhandled exception:
494
+
495
+ - [post_mortem_example.py](https://github.com/AlDanial/tdb/blob/main/examples/post_mortem_example.py)
496
+ - [post_mortem_entrypoint.sh](https://github.com/AlDanial/tdb/blob/main/examples/post_mortem_entrypoint.sh)
497
+ - [Dockerfile.post_mortem](https://github.com/AlDanial/tdb/blob/main/examples/Dockerfile.post_mortem)
498
+
379
499
  ### Live Breakpoint Hook
380
500
 
381
501
  `tdb` has an improved implemenation of the standard `breakpoint()` function (or equivalently,
@@ -673,7 +793,7 @@ curl -s -X POST http://127.0.0.1:8150/rpc \
673
793
  | `set_breakpoint` | `["file:line"]` or `["file:line", "condition", "hit_condition"]` | Set a breakpoint |
674
794
  | `remove_breakpoint` | `["file:line"]` | Remove a breakpoint |
675
795
  | `list_breakpoints` | `[]` | Show all breakpoints |
676
- | `continue` | `[]` or `[timeout_s]` | Resume execution; on timeout returns `"still runningcall pause or wait again"` (success) |
796
+ | `continue` | `[]` or `[timeout_s]` | Resume execution; on timeout returns `"still running--call pause or wait again"` (success) |
677
797
  | `next` | `[]` or `[timeout_s]` | Step over |
678
798
  | `step_in` | `[]` or `[timeout_s]` | Step into |
679
799
  | `step_out` | `[]` or `[timeout_s]` | Step out |
@@ -712,7 +832,7 @@ Each is JSON with `event`, `data`, and `timestamp` fields.
712
832
  tdb ships a Model Context Protocol (MCP) server (`tdb-mcp`) that exposes
713
833
  the debugger as a curated set of tools an AI agent can call. The MCP
714
834
  server is a third in-process consumer of the same dispatch handlers the
715
- TUI and the HTTP server use so an agent gets the same lock semantics,
835
+ TUI and the HTTP server use so an agent gets the same lock semantics,
716
836
  including the pause-during-continue bypass, and the same DAP-backed
717
837
  inspection surface.
718
838
 
@@ -751,17 +871,24 @@ client expects to launch servers.
751
871
  | Breakpoints | `set_breakpoint(spec, condition?, hit_condition?)`, `remove_breakpoint(spec)`, `list_breakpoints()` |
752
872
  | Differentiators | `threads(thread_id?)`, `tasks(task_name?)`, `processes(name_or_pid?)`, `wait_graph()` |
753
873
 
754
- `control` is intentionally one tool that takes an action enum the six
874
+ `control` is intentionally one tool that takes an action enum. The six
755
875
  underlying RPC actions share a return shape, and agents perform
756
876
  measurably better with a small surface than with one tool per action.
757
877
  `threads` / `tasks` / `processes` overload list-vs-inspect via a
758
878
  single optional argument for the same reason.
759
879
 
880
+ `debug_launch` accepts optional `lang` and `adapter` parameters mirroring the
881
+ CLI's `--lang`/`--adapter`; when omitted, the language is auto-detected from
882
+ `program`, so an agent can hand it a compiled binary directly. The
883
+ `tasks`/`processes`/`wait_graph` tools stay registered for every language but
884
+ return a structured "not supported when debugging C/C++"-style error for
885
+ non-Python debuggees.
886
+
760
887
  ### Agent flow for a long-running step
761
888
 
762
889
  ```
763
890
  agent → control(action="continue", timeout_s=30)
764
- mcp → "still running call pause or wait again"
891
+ mcp → "still running, call pause or wait again"
765
892
  agent → control(action="pause") # OR: control(action="wait_for_stop", timeout_s=30)
766
893
  mcp → "<file>:<line>"
767
894
  agent → inspect(["x", "len(buf)"])
@@ -770,7 +897,7 @@ mcp → "x = 7\nlen(buf) = 1024"
770
897
 
771
898
  `pause` bypasses the dispatch lock so it can interrupt a `continue`
772
899
  that's still mid-flight (HTTP and MCP share the same `NO_LOCK_ACTIONS`
773
- policy see `tdb/server/app.py`).
900
+ policy; see `tdb/server/app.py`).
774
901
 
775
902
  ### Security caveat
776
903
 
@@ -779,22 +906,23 @@ execution in the debuggee process**. This is inherent to a debugger and
779
906
  not a tdb-specific concern, but MCP clients (and the humans running
780
907
  them) should apply appropriate permission models: don't auto-approve
781
908
  `inspect` against untrusted expressions, and don't expose `tdb-mcp` on
782
- a network stdio transport only by design.
909
+ a network (stdio transport only by design).
783
910
 
784
911
  ### Deferred / out of scope (v1)
785
912
 
786
- - SSE-style event push `control` and `wait_for_stop` make polling
913
+ - SSE-style event push: `control` and `wait_for_stop` make polling
787
914
  efficient enough; events would also need uneven MCP-client support.
788
- - HTTP / streamable-HTTP transports would require auth (which the
915
+ - HTTP / streamable-HTTP transports: would require auth (which the
789
916
  HTTP RPC server also currently lacks); stdio inherits the trust of
790
917
  the process that spawned it.
791
- - Multi-session one debug session per MCP process.
918
+ - Multi-session: one debug session per MCP process.
792
919
 
793
920
  ## CLI Reference
794
921
 
795
922
  ```
796
923
  usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
797
924
  [--no-just-my-code] [--no-subprocess] [--python PYTHON] [--pv]
925
+ [--lang LANGUAGE] [--adapter ADAPTER]
798
926
  [--keybindings {default,vim,emacs}]
799
927
  [--terminal {xterm,konsole,gnome-terminal,ghostty,kitty,iterm2,warp,wezterm,terminator}]
800
928
  [--local-root PATH] [--remote-root PATH]
@@ -810,8 +938,10 @@ usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
810
938
  | `-k`, `--breakpoint FILE:LINE|LINE` | Set a breakpoint (may be repeated). Passing `-k` implies `--no-stop-on-entry` so the program runs straight to the first breakpoint. |
811
939
  | `--no-stop-on-entry` | Do not pause at the first line (default: stop on entry; automatic when `-k` is given) |
812
940
  | `--cwd DIR` | Working directory for the debuggee |
813
- | `--python PATH` | Python interpreter for the debuggee |
941
+ | `--python PATH` | Python interpreter for the debuggee (Python targets only) |
814
942
  | `--pv` | Shorthand for --python .venv/bin/python |
943
+ | `--lang LANGUAGE` | Debuggee language (`python`, `cpp`); default: auto-detect from the target |
944
+ | `--adapter ADAPTER` | Debug adapter within the language (e.g. `--lang cpp --adapter lldb-dap`); default: the language's standard adapter |
815
945
  | `--no-just-my-code` | Step into stdlib/site-packages code instead of skipping it
816
946
  (default: skipped). On uncaught exceptions, the crash modal always shows the full traceback
817
947
  including library frames, regardless of this flag. |
@@ -831,9 +961,14 @@ On Windows, it uses `%APPDATA%\tdb\`.
831
961
 
832
962
  | File | Contents |
833
963
  |------|----------|
834
- | `config.json` | User preferences (keybinding scheme, color theme, step mode) |
964
+ | `config.json` | User preferences (keybinding scheme, color theme, step mode, adapter overrides) |
835
965
  | `breakpoints.json` | Breakpoints from previous sessions, keyed by project directory |
836
966
 
967
+ Adapter-related keys in `config.json`: `adapters` maps an adapter id to an
968
+ executable path (`{"adapters": {"lldb-dap": "/opt/llvm/bin/lldb-dap"}}`), and
969
+ `default_adapters` picks a language's default adapter
970
+ (`{"default_adapters": {"cpp": "lldb-dap"}}`).
971
+
837
972
  Breakpoints are saved on exit and restored when debugging a program in the same
838
973
  directory. Each project's breakpoints are independent.
839
974
 
@@ -853,7 +988,8 @@ fires as expected.
853
988
  ## Tech Stack
854
989
 
855
990
  - [textual](https://github.com/Textualize/textual) : TUI framework
856
- - [debugpy](https://github.com/microsoft/debugpy) : Debug Adapter Protocol implementation
991
+ - [debugpy](https://github.com/microsoft/debugpy) : Debug Adapter Protocol implementation for Python
992
+ - [gdb](https://sourceware.org/gdb/) / [lldb-dap](https://lldb.llvm.org/resources/lldbdap.html) : optional, user-installed DAP adapters for C/C++
857
993
  - [pygments](https://pygments.org/) : Syntax highlighting
858
994
  - [FastAPI](https://fastapi.tiangolo.com/) + [uvicorn](https://www.uvicorn.org/) : JSON-RPC server
859
995