textual-debugger 0.1.5__tar.gz → 0.2.2__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 (89) hide show
  1. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/PKG-INFO +260 -22
  2. {textual_debugger-0.1.5/src/tdb → textual_debugger-0.2.2}/README.md +258 -20
  3. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/pyproject.toml +2 -2
  4. {textual_debugger-0.1.5 → textual_debugger-0.2.2/src/tdb}/README.md +258 -20
  5. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/__init__.py +1 -1
  6. textual_debugger-0.2.2/src/tdb/adapters/perl/Devel/TdbRemote.pm +93 -0
  7. textual_debugger-0.2.2/src/tdb/adapters/perl/__main__.py +23 -0
  8. textual_debugger-0.2.2/src/tdb/adapters/perl/helpers.pl +384 -0
  9. textual_debugger-0.2.2/src/tdb/adapters/perl/protocol.py +110 -0
  10. textual_debugger-0.2.2/src/tdb/adapters/perl/refs.py +34 -0
  11. textual_debugger-0.2.2/src/tdb/adapters/perl/server.py +593 -0
  12. textual_debugger-0.2.2/src/tdb/adapters/perl/session.py +247 -0
  13. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/app.py +231 -27
  14. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/app_handlers/dap_events.py +2 -0
  15. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/app_handlers/inspection.py +56 -8
  16. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/cli.py +216 -57
  17. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/dap/client.py +45 -75
  18. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/dap/messages.py +20 -0
  19. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/dap/types.py +3 -0
  20. textual_debugger-0.2.2/src/tdb/languages/__init__.py +21 -0
  21. textual_debugger-0.2.2/src/tdb/languages/base.py +144 -0
  22. textual_debugger-0.2.2/src/tdb/languages/cpp.py +150 -0
  23. textual_debugger-0.2.2/src/tdb/languages/perl.py +98 -0
  24. textual_debugger-0.2.2/src/tdb/languages/python.py +116 -0
  25. textual_debugger-0.2.2/src/tdb/languages/registry.py +119 -0
  26. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/mcp/server.py +12 -2
  27. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/mcp/session.py +51 -10
  28. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/persist.py +27 -11
  29. textual_debugger-0.2.2/src/tdb/replay.py +202 -0
  30. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/server/handlers.py +23 -5
  31. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/server/runner.py +76 -27
  32. textual_debugger-0.2.2/src/tdb/session/__init__.py +0 -0
  33. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/session/child_processes.py +7 -0
  34. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/session/controller.py +75 -20
  35. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/session/inspect_service.py +14 -2
  36. textual_debugger-0.2.2/src/tdb/session/recorder.py +106 -0
  37. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/session/state.py +15 -0
  38. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/session/statement_stepper.py +16 -3
  39. textual_debugger-0.2.2/src/tdb/widgets/__init__.py +0 -0
  40. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/widgets/code_view.py +94 -54
  41. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/textual_debugger.egg-info/PKG-INFO +260 -22
  42. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/textual_debugger.egg-info/SOURCES.txt +17 -0
  43. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/textual_debugger.egg-info/requires.txt +1 -1
  44. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/LICENSE +0 -0
  45. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/setup.cfg +0 -0
  46. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/__main__.py +0 -0
  47. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/_timeouts.py +0 -0
  48. {textual_debugger-0.1.5/src/tdb/dap → textual_debugger-0.2.2/src/tdb/adapters}/__init__.py +0 -0
  49. {textual_debugger-0.1.5/src/tdb/server → textual_debugger-0.2.2/src/tdb/adapters/perl}/__init__.py +0 -0
  50. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/app_handlers/__init__.py +0 -0
  51. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/app_handlers/routing.py +0 -0
  52. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/app_handlers/ui_panels.py +0 -0
  53. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/app_helpers.py +0 -0
  54. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/breakpoint_hook.py +0 -0
  55. {textual_debugger-0.1.5/src/tdb/session → textual_debugger-0.2.2/src/tdb/dap}/__init__.py +0 -0
  56. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/dap/protocol.py +0 -0
  57. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/inspection.py +0 -0
  58. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/inspection_full.py +0 -0
  59. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/keybindings/__init__.py +0 -0
  60. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/mcp/__init__.py +0 -0
  61. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/mcp/__main__.py +0 -0
  62. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/post_mortem.py +0 -0
  63. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/processes_cache.py +0 -0
  64. {textual_debugger-0.1.5/src/tdb/widgets → textual_debugger-0.2.2/src/tdb/server}/__init__.py +0 -0
  65. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/server/app.py +0 -0
  66. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/server/event_handler.py +0 -0
  67. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/server/rpc_types.py +0 -0
  68. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/session/event_bus.py +0 -0
  69. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/session/messages.py +0 -0
  70. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/session/post_mortem_loader.py +0 -0
  71. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/session/terminal.py +0 -0
  72. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/session/textual_handler.py +0 -0
  73. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/source_analysis.py +0 -0
  74. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/widgets/_inspection_modal.py +0 -0
  75. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/widgets/async_tasks_modal.py +0 -0
  76. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/widgets/breakpoint_view.py +0 -0
  77. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/widgets/console_view.py +0 -0
  78. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/widgets/evaluate_console.py +0 -0
  79. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/widgets/full_contents_modal.py +0 -0
  80. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/widgets/menu_bar.py +0 -0
  81. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/widgets/modals.py +0 -0
  82. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/widgets/processes_modal.py +0 -0
  83. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/widgets/stack_view.py +0 -0
  84. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/widgets/status_bar.py +0 -0
  85. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/widgets/threads_modal.py +0 -0
  86. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/tdb/widgets/variable_view.py +0 -0
  87. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/textual_debugger.egg-info/dependency_links.txt +0 -0
  88. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/src/textual_debugger.egg-info/entry_points.txt +0 -0
  89. {textual_debugger-0.1.5 → textual_debugger-0.2.2}/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.5
3
+ Version: 0.2.2
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:
@@ -51,7 +51,7 @@ Requires-Dist: debugpy>=1.8.0
51
51
  Requires-Dist: pygments>=2.17.0
52
52
  Requires-Dist: fastapi>=0.115.0
53
53
  Requires-Dist: uvicorn>=0.30.0
54
- Requires-Dist: mcp>=1.28.0
54
+ Requires-Dist: mcp<2.0.0,>=1.28.0
55
55
  Provides-Extra: dev
56
56
  Requires-Dist: pytest; extra == "dev"
57
57
  Requires-Dist: pytest-cov; extra == "dev"
@@ -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)
@@ -169,11 +179,25 @@ tdb my_program.py
169
179
  # debug with arguments
170
180
  tdb my_program.py arg1 arg2
171
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
+
172
192
  # add breakpoints at lines 20 and 35 of `my_program.py` and line 14
173
193
  # of `module.py` (when -k is given, --no-stop-on-entry is set and the
174
194
  # program runs to the first breakpoint)
175
195
  tdb -k 20 -k 35 -k module.py:14 my_program.py arg1 arg2
176
196
 
197
+ # run straight to line 20 without saving the breakpoint for future
198
+ # sessions (-t is -k minus the persistence)
199
+ tdb -t 20 my_program.py
200
+
177
201
  # use a specific virtualenv
178
202
  tdb --python /path/to/venv/bin/python my_program.py
179
203
 
@@ -210,6 +234,148 @@ Alternatively, use the module entry point:
210
234
  python -m tdb my_program.py
211
235
  ```
212
236
 
237
+ ## Multi-Language Debugging
238
+
239
+ `tdb` debugs any language that has a Debug Adapter Protocol backend. Two
240
+ languages are supported out of the box:
241
+
242
+ | Language | Adapter(s) | How to get the adapter | Feature level |
243
+ |----------|------------|------------------------|---------------|
244
+ | Python | `debugpy` (default) | installed with `textual-debugger` | everything in this README |
245
+ | 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 |
246
+ | Perl | perl-tdb (bundled) | needs perl ≥ 5.18 on PATH (or `{"adapters": {"perl": ...}}`) | core debugging + remote attach |
247
+
248
+ ### Language detection and selection
249
+
250
+ The language is auto-detected from the debug target:
251
+
252
+ 1. File extension: `.py` → Python; `.pl` / `.pm` / `.t` → Perl.
253
+ 2. Native executables (ELF, Mach-O, PE magic bytes) → C/C++.
254
+ 3. A `#!...python` or `#!...perl` shebang → Python / Perl respectively.
255
+ 4. C/C++/Rust *source* files (`.c`, `.cpp`, `.rs`, …) produce an error with a
256
+ hint: compile with debug info (`g++ -g -O0`) and debug the binary.
257
+ 5. Anything else produces an error naming the `--lang` override.
258
+
259
+ `--lang` forces the language; `--adapter` picks a non-default adapter within
260
+ it (`tdb --lang cpp --adapter lldb-dap ./myprog`).
261
+
262
+ > **Migration note:** extensionless Python scripts without a `python` shebang
263
+ > were previously assumed to be Python; they now require `--lang python`.
264
+
265
+ ### Adapters are found on `PATH`
266
+
267
+ `tdb` does not download or bundle adapters. If the adapter executable isn't
268
+ found, the error names the package to install. To use an adapter from a
269
+ non-standard location, or change a language's default adapter, add to
270
+ `config.json` (see [Configuration](#configuration)):
271
+
272
+ ```json
273
+ {
274
+ "adapters": {"lldb-dap": "/opt/llvm/bin/lldb-dap"},
275
+ "default_adapters": {"cpp": "lldb-dap"}
276
+ }
277
+ ```
278
+
279
+ ### What works for non-Python languages
280
+
281
+ Core debugging works identically for every language: breakpoints (incl.
282
+ conditions and persistence), stepping, continue/pause, run-to-cursor, stack
283
+ navigation, variable inspection, the evaluate console, syntax highlighting,
284
+ and the JSON-RPC / MCP programmatic modes.
285
+
286
+ Python-specific features are hidden or return a clear "not supported for this
287
+ language" message when debugging other languages: statement-granularity
288
+ stepping (non-Python languages always step per line), the async task /
289
+ process inspectors and wait graph, the evaluate console's trailing-`?` help,
290
+ `--python`/`--pv`, `--no-subprocess`, automatic child-process attachment, and
291
+ the post-mortem / `tdb.breakpoint()` hooks (those hooks live inside Python
292
+ programs by nature). Remote attach (`-r`) also works for Perl (see
293
+ [Perl](#perl) — `Devel::TdbRemote` in place of `debugpy.listen()`), but not
294
+ for C/C++. `--terminal` is
295
+ currently ignored for non-Python targets.
296
+
297
+ ### C/C++ tips
298
+
299
+ - Compile with `-g` (ideally `-g -O0`). If no breakpoint in a file can be
300
+ bound, `tdb` prints a console warning suggesting the program may lack
301
+ debug info.
302
+ - Stack frames pointing into system libraries often have no source on disk;
303
+ the Code View shows a `<Could not read …>` placeholder while the stack,
304
+ variables, and evaluate console remain fully usable.
305
+ - GDB (the default adapter) has the most complete libstdc++
306
+ pretty-printing. `lldb-dap` (via `--adapter lldb-dap`) also debugs
307
+ GCC-built binaries fine — DWARF is compiler-neutral.
308
+ - **GDB evaluate-console quirk:** GDB's DAP treats REPL input as CLI
309
+ commands, so evaluate expressions with an explicit `print`, e.g.
310
+ `print x` rather than bare `x` (bare `x` collides with GDB's
311
+ examine-memory command). `lldb-dap` evaluates bare expressions directly.
312
+
313
+ ### Perl
314
+
315
+ `tdb` bundles its own Perl adapter (`perl-tdb`) — no separate adapter install
316
+ needed, just a `perl` ≥ 5.18 on `PATH`. It drives stock `perl5db` under the
317
+ hood, so it works with any Perl already on the system.
318
+
319
+ **Launching a script:**
320
+
321
+ ```bash
322
+ tdb script.pl
323
+ ```
324
+
325
+ **Remote attach:** useful when the Perl process is already running (a long-
326
+ lived service, a process started by something other than `tdb`) or lives on
327
+ another host/container. Add three lines to the target program, with the
328
+ `use` line first so the debugger is armed before any of your code compiles:
329
+
330
+ ```perl
331
+ use Devel::TdbRemote; # FIRST line of your program
332
+ ...
333
+ Devel::TdbRemote::listen(5678); # non-blocking
334
+ Devel::TdbRemote::wait_for_client(); # blocks until tdb connects
335
+ ```
336
+
337
+ Then attach from `tdb`, forcing the language since there's no local `program`
338
+ argument for `tdb` to detect it from:
339
+
340
+ ```bash
341
+ tdb --lang perl -r host:5678
342
+ ```
343
+
344
+ **Arming caveat:** only code *compiled after* the debugger is armed can be
345
+ stepped into or breakpointed. That's why `use Devel::TdbRemote;` must be the
346
+ first line of the program. If you can't edit the first line (e.g. a wrapper
347
+ script controls startup), arm it before Perl even parses your file instead:
348
+ `perl -d:TdbRemote prog.pl`, or set `PERL5OPT=-d:TdbRemote` in the
349
+ environment that launches the debuggee.
350
+
351
+ **Copying the adapter to a remote host:** `Devel::TdbRemote` and its helper
352
+ script are plain files, not a CPAN install — copy both onto the remote
353
+ machine and point `PERL5LIB` at the directory that contains them:
354
+
355
+ ```bash
356
+ # From a checkout or an installed wheel's site-packages/tdb/adapters/perl:
357
+ scp -r Devel/TdbRemote.pm helpers.pl remote-host:/opt/tdb-perl/
358
+ # On the remote host:
359
+ export PERL5LIB=/opt/tdb-perl:$PERL5LIB
360
+ ```
361
+
362
+ (`Devel/TdbRemote.pm` locates `helpers.pl` next to itself at runtime, so keep
363
+ the two files in the same relative layout shown above — `helpers.pl` is a
364
+ sibling of the `Devel/` directory, not inside it.)
365
+
366
+ **PadWalker (optional but recommended):** inspecting lexical (`my`)
367
+ variables in the *current* frame always works. Lexicals in outer/caller
368
+ frames need the `PadWalker` module installed on the debuggee's Perl; without
369
+ it, tdb falls back to a read-only pad walk that can't reach fully into
370
+ enclosing scopes, and outer-frame variable listings degrade accordingly.
371
+ Install with `cpanm PadWalker` (or your distro's package) for full fidelity.
372
+
373
+ **Pause is unavailable in attach mode.** Launch-mode sessions (`tdb
374
+ script.pl`) support pausing a running program at any time. Remote-attach
375
+ sessions don't — debugpy-style asynchronous pause needs a control channel
376
+ `Devel::TdbRemote` doesn't implement yet; `pause` in attach mode returns a
377
+ clear "not available" error instead of hanging.
378
+
213
379
  ## Layout
214
380
 
215
381
  ```
@@ -238,7 +404,7 @@ The footer shows the most relevant keybindings for the current mode.
238
404
  ### Navigation and Keybindings
239
405
 
240
406
 
241
- The Code View shows syntax-highlighted Python source with line numbers.
407
+ The Code View shows syntax-highlighted source (lexer chosen per language) with line numbers.
242
408
  A cursor line (blue) tracks your position; the current execution line is highlighted in gold.
243
409
 
244
410
  **View focus shortcuts (global):**
@@ -330,6 +496,9 @@ lands on `print(results)`, not on each interior sub-line of the `gather` call. S
330
496
  stops on each physical line--useful for inspecting how a complex expression is built up.
331
497
  The choice is saved to `~/.config/tdb/config.json`.
332
498
 
499
+ Statement mode requires a source-language model and is currently Python-only;
500
+ other languages always step per line (the Step Mode menu says so if you try).
501
+
333
502
  ### Breakpoints
334
503
 
335
504
  Click the gutter in the Code View to toggle a breakpoint, or press `b` in Debug mode.
@@ -387,6 +556,15 @@ interactive evaluation of expressions in the current scope:
387
556
 
388
557
  Variable values set here are reflected in the running code.
389
558
 
559
+ ### Cut / Paste
560
+
561
+ Expression for the Evaluate Console are often copied from the Code View.
562
+ Doing this in `tdb` differs from traditional terminal behavior, because `textual` applications
563
+ capture mouse events for their own use.
564
+
565
+ Instead, hold the `Shift` key while performing your conventional cut/paste keystrokes or mouse
566
+ operation to get the expected behavior.
567
+
390
568
  ### Console Output
391
569
 
392
570
  The Console View captures stdout (normal text) and stderr (red text) from the debuggee
@@ -754,7 +932,7 @@ curl -s -X POST http://127.0.0.1:8150/rpc \
754
932
  | `set_breakpoint` | `["file:line"]` or `["file:line", "condition", "hit_condition"]` | Set a breakpoint |
755
933
  | `remove_breakpoint` | `["file:line"]` | Remove a breakpoint |
756
934
  | `list_breakpoints` | `[]` | Show all breakpoints |
757
- | `continue` | `[]` or `[timeout_s]` | Resume execution; on timeout returns `"still running — call pause or wait again"` (success) |
935
+ | `continue` | `[]` or `[timeout_s]` | Resume execution; on timeout returns `"still running--call pause or wait again"` (success) |
758
936
  | `next` | `[]` or `[timeout_s]` | Step over |
759
937
  | `step_in` | `[]` or `[timeout_s]` | Step into |
760
938
  | `step_out` | `[]` or `[timeout_s]` | Step out |
@@ -788,12 +966,46 @@ curl -N http://127.0.0.1:8150/events
788
966
  Events: `initialized`, `stopped`, `continued`, `terminated`, `exited`, `output`.
789
967
  Each is JSON with `event`, `data`, and `timestamp` fields.
790
968
 
969
+ ## Recording and replaying sessions
970
+
971
+ `tdb --record session.jsonl prog.py` runs a normal TUI session and captures
972
+ your debugging actions — breakpoints (including `-k`/`-t` and persisted
973
+ ones), stepping, continue/pause, Evaluate-console entries, stack-frame
974
+ navigation, variable expansion, restart, quit — to `session.jsonl` as
975
+ JSON-RPC commands. Works with launch mode (any language) and `-r`
976
+ remote attach.
977
+
978
+ Replay it two ways:
979
+
980
+ - `tdb --replay session.jsonl` — one command: launches the recorded
981
+ program headless, feeds every recorded command through the same RPC
982
+ dispatch `tdb --server` uses, and prints a transcript (recorded time,
983
+ command, verbatim result, interleaved program output). Exit code 0 iff
984
+ every command succeeded. Add `--timing` to reproduce the original
985
+ pacing, `--replay-timeout S` to bound each stop-wait (default 30 s).
986
+ - Against a live server: start `tdb --server prog.py`, then feed line 2
987
+ onward of the file to `POST /rpc` — each line is already a valid
988
+ request body:
989
+
990
+ tail -n +2 session.jsonl | while read line; do
991
+ curl -s -X POST -H 'Content-Type: application/json' \
992
+ -d "$line" http://127.0.0.1:8150/rpc
993
+ done
994
+
995
+ (On Windows, an equivalent loop in Python: read the file, skip the
996
+ first line, `requests.post` each remaining line.)
997
+
998
+ Not captured: pure viewing (scrolling, search, modals, thread/task
999
+ lists), breakpoint enable/disable toggles, variable expansions when the
1000
+ adapter reports no `evaluateName` (currently the Perl adapter), and
1001
+ File > Open program switches.
1002
+
791
1003
  ## MCP Integration
792
1004
 
793
1005
  tdb ships a Model Context Protocol (MCP) server (`tdb-mcp`) that exposes
794
1006
  the debugger as a curated set of tools an AI agent can call. The MCP
795
1007
  server is a third in-process consumer of the same dispatch handlers the
796
- TUI and the HTTP server use — so an agent gets the same lock semantics,
1008
+ TUI and the HTTP server use so an agent gets the same lock semantics,
797
1009
  including the pause-during-continue bypass, and the same DAP-backed
798
1010
  inspection surface.
799
1011
 
@@ -832,17 +1044,24 @@ client expects to launch servers.
832
1044
  | Breakpoints | `set_breakpoint(spec, condition?, hit_condition?)`, `remove_breakpoint(spec)`, `list_breakpoints()` |
833
1045
  | Differentiators | `threads(thread_id?)`, `tasks(task_name?)`, `processes(name_or_pid?)`, `wait_graph()` |
834
1046
 
835
- `control` is intentionally one tool that takes an action enum — the six
1047
+ `control` is intentionally one tool that takes an action enum. The six
836
1048
  underlying RPC actions share a return shape, and agents perform
837
1049
  measurably better with a small surface than with one tool per action.
838
1050
  `threads` / `tasks` / `processes` overload list-vs-inspect via a
839
1051
  single optional argument for the same reason.
840
1052
 
1053
+ `debug_launch` accepts optional `lang` and `adapter` parameters mirroring the
1054
+ CLI's `--lang`/`--adapter`; when omitted, the language is auto-detected from
1055
+ `program`, so an agent can hand it a compiled binary directly. The
1056
+ `tasks`/`processes`/`wait_graph` tools stay registered for every language but
1057
+ return a structured "not supported when debugging C/C++"-style error for
1058
+ non-Python debuggees.
1059
+
841
1060
  ### Agent flow for a long-running step
842
1061
 
843
1062
  ```
844
1063
  agent → control(action="continue", timeout_s=30)
845
- mcp → "still running — call pause or wait again"
1064
+ mcp → "still running, call pause or wait again"
846
1065
  agent → control(action="pause") # OR: control(action="wait_for_stop", timeout_s=30)
847
1066
  mcp → "<file>:<line>"
848
1067
  agent → inspect(["x", "len(buf)"])
@@ -851,7 +1070,7 @@ mcp → "x = 7\nlen(buf) = 1024"
851
1070
 
852
1071
  `pause` bypasses the dispatch lock so it can interrupt a `continue`
853
1072
  that's still mid-flight (HTTP and MCP share the same `NO_LOCK_ACTIONS`
854
- policy — see `tdb/server/app.py`).
1073
+ policy; see `tdb/server/app.py`).
855
1074
 
856
1075
  ### Security caveat
857
1076
 
@@ -860,22 +1079,23 @@ execution in the debuggee process**. This is inherent to a debugger and
860
1079
  not a tdb-specific concern, but MCP clients (and the humans running
861
1080
  them) should apply appropriate permission models: don't auto-approve
862
1081
  `inspect` against untrusted expressions, and don't expose `tdb-mcp` on
863
- a network — stdio transport only by design.
1082
+ a network (stdio transport only by design).
864
1083
 
865
1084
  ### Deferred / out of scope (v1)
866
1085
 
867
- - SSE-style event push — `control` and `wait_for_stop` make polling
1086
+ - SSE-style event push: `control` and `wait_for_stop` make polling
868
1087
  efficient enough; events would also need uneven MCP-client support.
869
- - HTTP / streamable-HTTP transports — would require auth (which the
1088
+ - HTTP / streamable-HTTP transports: would require auth (which the
870
1089
  HTTP RPC server also currently lacks); stdio inherits the trust of
871
1090
  the process that spawned it.
872
- - Multi-session — one debug session per MCP process.
1091
+ - Multi-session: one debug session per MCP process.
873
1092
 
874
1093
  ## CLI Reference
875
1094
 
876
1095
  ```
877
1096
  usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
878
1097
  [--no-just-my-code] [--no-subprocess] [--python PYTHON] [--pv]
1098
+ [--lang LANGUAGE] [--adapter ADAPTER]
879
1099
  [--keybindings {default,vim,emacs}]
880
1100
  [--terminal {xterm,konsole,gnome-terminal,ghostty,kitty,iterm2,warp,wezterm,terminator}]
881
1101
  [--local-root PATH] [--remote-root PATH]
@@ -889,10 +1109,13 @@ usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
889
1109
  | `--local-root PATH` | Local directory containing a copy of remote code (repeat to mirror multiple trees). Pair with `--remote-root`; counts must match. Required when `-k` sets a breakpoint against a remote debuggee whose code lives at a different path. |
890
1110
  | `--remote-root PATH` | Remote directory matched to `--local-root` (same CLI position via `zip()`). |
891
1111
  | `-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. |
1112
+ | `-t`, `--to-line FILE:LINE|LINE` | Like `-k`, but the breakpoint is not saved to the breakpoints file — it just takes you to that spot in the code for this session (may be repeated). |
892
1113
  | `--no-stop-on-entry` | Do not pause at the first line (default: stop on entry; automatic when `-k` is given) |
893
1114
  | `--cwd DIR` | Working directory for the debuggee |
894
- | `--python PATH` | Python interpreter for the debuggee |
1115
+ | `--python PATH` | Python interpreter for the debuggee (Python targets only) |
895
1116
  | `--pv` | Shorthand for --python .venv/bin/python |
1117
+ | `--lang LANGUAGE` | Debuggee language (`python`, `cpp`, `perl`); default: auto-detect from the target |
1118
+ | `--adapter ADAPTER` | Debug adapter within the language (e.g. `--lang cpp --adapter lldb-dap`); default: the language's standard adapter |
896
1119
  | `--no-just-my-code` | Step into stdlib/site-packages code instead of skipping it
897
1120
  (default: skipped). On uncaught exceptions, the crash modal always shows the full traceback
898
1121
  including library frames, regardless of this flag. |
@@ -912,11 +1135,25 @@ On Windows, it uses `%APPDATA%\tdb\`.
912
1135
 
913
1136
  | File | Contents |
914
1137
  |------|----------|
915
- | `config.json` | User preferences (keybinding scheme, color theme, step mode) |
1138
+ | `config.json` | User preferences (keybinding scheme, color theme, step mode, adapter overrides) |
916
1139
  | `breakpoints.json` | Breakpoints from previous sessions, keyed by project directory |
917
1140
 
1141
+ Adapter-related keys in `config.json`: `adapters` maps an adapter id to an
1142
+ executable path (`{"adapters": {"lldb-dap": "/opt/llvm/bin/lldb-dap"}}`), and
1143
+ `default_adapters` picks a language's default adapter
1144
+ (`{"default_adapters": {"cpp": "lldb-dap"}}`).
1145
+
1146
+ **Perl is a special case:** `perl-tdb` is tdb's own bundled adapter (always
1147
+ found — it's Python code, not an external executable), so
1148
+ `adapters.perl` doesn't select an adapter binary. Instead it names the
1149
+ *Perl interpreter* tdb should spawn to run the debuggee:
1150
+ `{"adapters": {"perl": "/path/to/perl"}}`. Use this when the `perl` on
1151
+ `PATH` is too old (< 5.18) or you need a specific `perlbrew`/`plenv` version.
1152
+
918
1153
  Breakpoints are saved on exit and restored when debugging a program in the same
919
- directory. Each project's breakpoints are independent.
1154
+ directory. Each project's breakpoints are independent. Breakpoints set with
1155
+ `-t`/`--to-line` are the exception: they behave like `-k` breakpoints during
1156
+ the session but are never saved.
920
1157
 
921
1158
  **Step mode** (`step_mode` in `config.json`) controls how `n` (step over) and `s` (step
922
1159
  into) handle multi-line source statements:
@@ -934,7 +1171,8 @@ fires as expected.
934
1171
  ## Tech Stack
935
1172
 
936
1173
  - [textual](https://github.com/Textualize/textual) : TUI framework
937
- - [debugpy](https://github.com/microsoft/debugpy) : Debug Adapter Protocol implementation
1174
+ - [debugpy](https://github.com/microsoft/debugpy) : Debug Adapter Protocol implementation for Python
1175
+ - [gdb](https://sourceware.org/gdb/) / [lldb-dap](https://lldb.llvm.org/resources/lldbdap.html) : optional, user-installed DAP adapters for C/C++
938
1176
  - [pygments](https://pygments.org/) : Syntax highlighting
939
1177
  - [FastAPI](https://fastapi.tiangolo.com/) + [uvicorn](https://www.uvicorn.org/) : JSON-RPC server
940
1178