textual-debugger 0.2.2__tar.gz → 0.2.6__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 (116) hide show
  1. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/PKG-INFO +277 -42
  2. {textual_debugger-0.2.2/src/tdb → textual_debugger-0.2.6}/README.md +271 -38
  3. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/pyproject.toml +6 -4
  4. {textual_debugger-0.2.2 → textual_debugger-0.2.6/src/tdb}/README.md +271 -38
  5. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/__init__.py +1 -1
  6. textual_debugger-0.2.6/src/tdb/adapters/bash/__main__.py +23 -0
  7. textual_debugger-0.2.6/src/tdb/adapters/bash/declares.py +92 -0
  8. textual_debugger-0.2.6/src/tdb/adapters/bash/server.py +472 -0
  9. textual_debugger-0.2.6/src/tdb/adapters/bash/session.py +593 -0
  10. textual_debugger-0.2.6/src/tdb/adapters/bash/tdb_harness.sh +240 -0
  11. textual_debugger-0.2.6/src/tdb/adapters/perl/Devel/TdbCompile.pm +68 -0
  12. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/adapters/perl/helpers.pl +64 -1
  13. textual_debugger-0.2.6/src/tdb/adapters/perl/server.py +1158 -0
  14. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/adapters/perl/session.py +164 -21
  15. textual_debugger-0.2.6/src/tdb/adapters/tcsh/__init__.py +7 -0
  16. textual_debugger-0.2.6/src/tdb/adapters/tcsh/__main__.py +3 -0
  17. textual_debugger-0.2.6/src/tdb/adapters/tcsh/breakpoints.py +50 -0
  18. textual_debugger-0.2.6/src/tdb/adapters/tcsh/cli.py +163 -0
  19. textual_debugger-0.2.6/src/tdb/adapters/tcsh/guardian.py +888 -0
  20. textual_debugger-0.2.6/src/tdb/adapters/tcsh/inspect.py +193 -0
  21. textual_debugger-0.2.6/src/tdb/adapters/tcsh/instrumenter.py +368 -0
  22. textual_debugger-0.2.6/src/tdb/adapters/tcsh/models.py +62 -0
  23. textual_debugger-0.2.6/src/tdb/adapters/tcsh/protocol.py +68 -0
  24. textual_debugger-0.2.6/src/tdb/adapters/tcsh/runtime.py +314 -0
  25. textual_debugger-0.2.6/src/tdb/adapters/tcsh/scanner.py +225 -0
  26. textual_debugger-0.2.6/src/tdb/adapters/tcsh/server.py +813 -0
  27. textual_debugger-0.2.6/src/tdb/adapters/tcsh/session.py +1576 -0
  28. textual_debugger-0.2.6/src/tdb/adapters/tcsh/transport.py +1000 -0
  29. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/app.py +108 -21
  30. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/app_handlers/dap_events.py +61 -64
  31. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/app_handlers/ui_panels.py +2 -0
  32. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/cli.py +70 -0
  33. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/dap/client.py +13 -1
  34. textual_debugger-0.2.6/src/tdb/dap/reverse.py +54 -0
  35. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/dap/types.py +19 -0
  36. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/languages/base.py +52 -0
  37. textual_debugger-0.2.6/src/tdb/languages/bash.py +84 -0
  38. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/languages/cpp.py +12 -1
  39. textual_debugger-0.2.6/src/tdb/languages/errors.py +235 -0
  40. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/languages/perl.py +6 -2
  41. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/languages/python.py +3 -1
  42. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/languages/registry.py +18 -0
  43. textual_debugger-0.2.6/src/tdb/languages/tcsh.py +102 -0
  44. textual_debugger-0.2.6/src/tdb/run_mode.py +271 -0
  45. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/server/event_handler.py +0 -3
  46. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/session/controller.py +92 -12
  47. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/session/event_bus.py +49 -0
  48. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/session/state.py +7 -0
  49. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/session/textual_handler.py +0 -1
  50. textual_debugger-0.2.6/src/tdb/widgets/__init__.py +0 -0
  51. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/widgets/async_tasks_modal.py +1 -1
  52. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/widgets/code_view.py +0 -1
  53. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/widgets/evaluate_console.py +2 -3
  54. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/widgets/modals.py +55 -1
  55. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/textual_debugger.egg-info/PKG-INFO +277 -42
  56. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/textual_debugger.egg-info/SOURCES.txt +26 -0
  57. textual_debugger-0.2.2/src/tdb/adapters/perl/server.py +0 -593
  58. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/LICENSE +0 -0
  59. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/setup.cfg +0 -0
  60. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/__main__.py +0 -0
  61. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/_timeouts.py +0 -0
  62. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/adapters/__init__.py +0 -0
  63. {textual_debugger-0.2.2/src/tdb/adapters/perl → textual_debugger-0.2.6/src/tdb/adapters/bash}/__init__.py +0 -0
  64. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/adapters/perl/Devel/TdbRemote.pm +0 -0
  65. {textual_debugger-0.2.2/src/tdb/dap → textual_debugger-0.2.6/src/tdb/adapters/perl}/__init__.py +0 -0
  66. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/adapters/perl/__main__.py +0 -0
  67. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/adapters/perl/protocol.py +0 -0
  68. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/adapters/perl/refs.py +0 -0
  69. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/app_handlers/__init__.py +0 -0
  70. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/app_handlers/inspection.py +0 -0
  71. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/app_handlers/routing.py +0 -0
  72. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/app_helpers.py +0 -0
  73. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/breakpoint_hook.py +0 -0
  74. {textual_debugger-0.2.2/src/tdb/server → textual_debugger-0.2.6/src/tdb/dap}/__init__.py +0 -0
  75. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/dap/messages.py +0 -0
  76. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/dap/protocol.py +0 -0
  77. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/inspection.py +0 -0
  78. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/inspection_full.py +0 -0
  79. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/keybindings/__init__.py +0 -0
  80. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/languages/__init__.py +0 -0
  81. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/mcp/__init__.py +0 -0
  82. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/mcp/__main__.py +0 -0
  83. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/mcp/server.py +0 -0
  84. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/mcp/session.py +0 -0
  85. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/persist.py +0 -0
  86. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/post_mortem.py +0 -0
  87. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/processes_cache.py +0 -0
  88. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/replay.py +0 -0
  89. {textual_debugger-0.2.2/src/tdb/session → textual_debugger-0.2.6/src/tdb/server}/__init__.py +0 -0
  90. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/server/app.py +0 -0
  91. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/server/handlers.py +0 -0
  92. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/server/rpc_types.py +0 -0
  93. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/server/runner.py +0 -0
  94. {textual_debugger-0.2.2/src/tdb/widgets → textual_debugger-0.2.6/src/tdb/session}/__init__.py +0 -0
  95. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/session/child_processes.py +0 -0
  96. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/session/inspect_service.py +0 -0
  97. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/session/messages.py +0 -0
  98. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/session/post_mortem_loader.py +0 -0
  99. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/session/recorder.py +0 -0
  100. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/session/statement_stepper.py +0 -0
  101. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/session/terminal.py +0 -0
  102. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/source_analysis.py +0 -0
  103. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/widgets/_inspection_modal.py +0 -0
  104. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/widgets/breakpoint_view.py +0 -0
  105. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/widgets/console_view.py +0 -0
  106. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/widgets/full_contents_modal.py +0 -0
  107. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/widgets/menu_bar.py +0 -0
  108. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/widgets/processes_modal.py +0 -0
  109. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/widgets/stack_view.py +0 -0
  110. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/widgets/status_bar.py +0 -0
  111. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/widgets/threads_modal.py +0 -0
  112. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/tdb/widgets/variable_view.py +0 -0
  113. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/textual_debugger.egg-info/dependency_links.txt +0 -0
  114. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/textual_debugger.egg-info/entry_points.txt +0 -0
  115. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/textual_debugger.egg-info/requires.txt +0 -0
  116. {textual_debugger-0.2.2 → textual_debugger-0.2.6}/src/textual_debugger.egg-info/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: textual-debugger
3
- Version: 0.2.2
4
- Summary: A TUI Python debugger based on textual and debugpy
3
+ Version: 0.2.6
4
+ Summary: A TUI debugger for Python, Perl, and other languages. Based on textual.
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:
7
7
 
@@ -39,11 +39,13 @@ Classifier: Intended Audience :: Developers
39
39
  Classifier: License :: OSI Approved :: MIT License
40
40
  Classifier: Operating System :: POSIX :: Linux
41
41
  Classifier: Programming Language :: Python :: 3
42
- Classifier: Programming Language :: Python :: 3.10
43
42
  Classifier: Programming Language :: Python :: 3.11
44
43
  Classifier: Programming Language :: Python :: 3.12
44
+ Classifier: Programming Language :: Python :: 3.13
45
+ Classifier: Programming Language :: Python :: 3.14
46
+ Classifier: Programming Language :: Python :: 3.15
45
47
  Classifier: Topic :: Software Development :: Debuggers
46
- Requires-Python: >=3.10
48
+ Requires-Python: >=3.11
47
49
  Description-Content-Type: text/markdown
48
50
  License-File: LICENSE
49
51
  Requires-Dist: textual>=8.0.0
@@ -68,8 +70,12 @@ Dynamic: license-file
68
70
 
69
71
  `textual-debugger` (the package) provides `tdb` (the command-line tool and module),
70
72
  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
+ with a Debug Adapter Protocol (DAP) implementation. In addition to Python,
74
+ `tdb` comes with built-in support for
75
+ - C and C++ (via `gdb` or `lldb-dap`)
76
+ - Perl (via `perl -d`)
77
+ - Bash (via bash's own `DEBUG` trap; bash ≥ 4.4)
78
+ - Tcsh (via source instrumentation of a stock `tcsh`)
73
79
 
74
80
  `tdb` is built with [textual](https://github.com/Textualize/textual) and speaks
75
81
  DAP to a pluggable debug adapter: [debugpy](https://github.com/microsoft/debugpy)
@@ -90,7 +96,9 @@ MIT License. Copyright 2026 by Al Danial.
90
96
  `tdb`:
91
97
 
92
98
  - 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
99
+ the richest feature set), C/C++ (via `gdb -i dap` or `lldb-dap`), Perl (via `perl -d`),
100
+ Bash (via bash's own `DEBUG` trap), and Tcsh (via source instrumentation of a
101
+ stock `tcsh`), with the language
94
102
  auto-detected from the target (ref. [Multi-Language Debugging](#multi-language-debugging)).
95
103
 
96
104
  - supports debugging of synchronous, asynchronous, multi-threaded, and multi-process Python code.
@@ -134,6 +142,8 @@ as open source.
134
142
  - OpenAI, for providing access to Codex through the
135
143
  [Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
136
144
 
145
+ This project was inspired by Andreas Klöckner's excellent [pudb](https://pypi.org/project/pudb/) debugger.
146
+
137
147
  ## Gallery
138
148
  <p align="center">
139
149
  <img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_breakpoint.png" alt="at breakpoint" width="300">
@@ -236,22 +246,26 @@ python -m tdb my_program.py
236
246
 
237
247
  ## Multi-Language Debugging
238
248
 
239
- `tdb` debugs any language that has a Debug Adapter Protocol backend. Two
249
+ `tdb` debugs any language that has a Debug Adapter Protocol backend. Five
240
250
  languages are supported out of the box:
241
251
 
242
- | Language | Adapter(s) | How to get the adapter | Feature level |
252
+ | Language | Adapter(s) | Dependencies | Feature level |
243
253
  |----------|------------|------------------------|---------------|
244
- | Python | `debugpy` (default) | installed with `textual-debugger` | everything in this README |
254
+ | Python | `debugpy` (default) | Python ≥ 3.11 | everything in this README |
245
255
  | 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 |
256
+ | Perl | perl-tdb (bundled) | perl ≥ 5.18 on PATH | core debugging + remote attach |
257
+ | Bash | bash-tdb (bundled) | bash ≥ 4.4 on PATH | core debugging (no remote attach) |
258
+ | Tcsh | tcsh-tdb (bundled) | tcsh on PATH | core debugging (no remote attach, no conditional breakpoints, no pause) |
247
259
 
248
260
  ### Language detection and selection
249
261
 
250
262
  The language is auto-detected from the debug target:
251
263
 
252
- 1. File extension: `.py` → Python; `.pl` / `.pm` / `.t` → Perl.
264
+ 1. File extension: `.py` → Python; `.pl` / `.pm` / `.t` → Perl; `.sh` / `.bash`
265
+ → Bash; `.csh` / `.tcsh` → Tcsh.
253
266
  2. Native executables (ELF, Mach-O, PE magic bytes) → C/C++.
254
- 3. A `#!...python` or `#!...perl` shebang → Python / Perl respectively.
267
+ 3. A `#!...python`, `#!...perl`, `#!...bash`, or `#!...csh`/`#!...tcsh`
268
+ shebang → Python / Perl / Bash / Tcsh respectively.
255
269
  4. C/C++/Rust *source* files (`.c`, `.cpp`, `.rs`, …) produce an error with a
256
270
  hint: compile with debug info (`g++ -g -O0`) and debug the binary.
257
271
  5. Anything else produces an error naming the `--lang` override.
@@ -283,16 +297,62 @@ conditions and persistence), stepping, continue/pause, run-to-cursor, stack
283
297
  navigation, variable inspection, the evaluate console, syntax highlighting,
284
298
  and the JSON-RPC / MCP programmatic modes.
285
299
 
286
- Python-specific features are hidden or return a clear "not supported for this
300
+ Python-specific features are hidden or return "not supported for this
287
301
  language" message when debugging other languages: statement-granularity
288
302
  stepping (non-Python languages always step per line), the async task /
289
303
  process inspectors and wait graph, the evaluate console's trailing-`?` help,
290
304
  `--python`/`--pv`, `--no-subprocess`, automatic child-process attachment, and
291
305
  the post-mortem / `tdb.breakpoint()` hooks (those hooks live inside Python
292
306
  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.
307
+ [Perl](#perl), `Devel::TdbRemote` in place of `debugpy.listen()`), but not
308
+ for C/C++, Bash, or Tcsh. `--terminal` works for every launch-mode
309
+ language — Python, Perl, Bash, Tcsh, and C/C++ via `--adapter lldb-dap` —
310
+ see [External Terminal Support](#external-terminal-support).
311
+
312
+ **Bash limitations (v1):** the bash adapter uses bash's own `DEBUG` trap and
313
+ has a smaller feature envelope than Python/Perl:
314
+
315
+ - Debuggee code that installs its own `DEBUG` trap clobbers the harness;
316
+ debugging silently degrades to free-running.
317
+ - No stopping inside subshells `(...)`, `$(...)`, or pipeline segments; they
318
+ execute normally.
319
+ - Child bash processes run uninstrumented.
320
+ - Outer-frame locals not inspectable (innermost frame only).
321
+ - Pause is deferred while blocked in an external command.
322
+ - `.sh` files that aren't bash are only diagnosed at launch, by bash itself
323
+ or the harness version check.
324
+ - The `DEBUG` trap never fires on a function-definition line, so a
325
+ breakpoint on a `func() {` line never hits; entry and step-in stops land
326
+ on the first executable line of the function body instead.
327
+ - The debug control channel occupies two inherited file descriptors
328
+ (typically high-numbered). A script that execs redirections onto those
329
+ exact fds (e.g. `exec 63>&-` or reusing them for its own I/O) will
330
+ silently break debugging.
331
+
332
+ See [Bash](#bash) below for launch details.
333
+
334
+ **Tcsh limitations (v1):** tcsh has no debug hooks at all, so the tcsh
335
+ adapter debugs an instrumented temporary *copy* of the script (original
336
+ paths and line numbers are preserved in everything tdb displays):
337
+
338
+ - Conditional breakpoints are not supported; a condition set in the
339
+ Breakpoint View is ignored for tcsh (the breakpoint always stops).
340
+ - No asynchronous pause: a free-running script stops only at the next
341
+ breakpoint (or when it exits).
342
+ - A breakpoint binds to the nearest safe statement at or after the
343
+ requested line; unplaceable breakpoints are reported unverified.
344
+ - Stack frames represent `source`d files, not native call frames, and only
345
+ literal `source` targets resolvable at launch are instrumented (computed
346
+ or `cd`-dependent sources run normally but are atomic to the debugger).
347
+ - All frames show the same live shell state (Shell Variables, Environment,
348
+ Aliases, Arguments). Stock tcsh keeps no per-frame history.
349
+ - Multiple commands on one physical line (`a ; b`), command substitutions,
350
+ and external commands are atomic stepping units.
351
+ - `$0` inside dynamically generated or evaluated text can expose the
352
+ generated copy's path (ordinary lexical `$0` is rewritten correctly).
353
+ - Requires Python ≥ 3.11.
354
+
355
+ See [Tcsh](#tcsh) below for launch details.
296
356
 
297
357
  ### C/C++ tips
298
358
 
@@ -304,16 +364,19 @@ currently ignored for non-Python targets.
304
364
  variables, and evaluate console remain fully usable.
305
365
  - GDB (the default adapter) has the most complete libstdc++
306
366
  pretty-printing. `lldb-dap` (via `--adapter lldb-dap`) also debugs
307
- GCC-built binaries fine — DWARF is compiler-neutral.
367
+ GCC-built binaries fine. DWARF is compiler-neutral.
308
368
  - **GDB evaluate-console quirk:** GDB's DAP treats REPL input as CLI
309
369
  commands, so evaluate expressions with an explicit `print`, e.g.
310
370
  `print x` rather than bare `x` (bare `x` collides with GDB's
311
371
  examine-memory command). `lldb-dap` evaluates bare expressions directly.
372
+ - `--terminal` (see [External Terminal Support](#external-terminal-support))
373
+ requires `--adapter lldb-dap`; GDB's DAP mode has no terminal integration
374
+ and `tdb` refuses `--terminal` with the default `gdb` adapter.
312
375
 
313
376
  ### Perl
314
377
 
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
378
+ `tdb` bundles its own Perl adapter (`perl-tdb`) so only need
379
+ `perl` ≥ 5.18 on `PATH`. It drives stock `perl5db` under the
317
380
  hood, so it works with any Perl already on the system.
318
381
 
319
382
  **Launching a script:**
@@ -322,15 +385,64 @@ hood, so it works with any Perl already on the system.
322
385
  tdb script.pl
323
386
  ```
324
387
 
388
+ `--terminal` is supported (see
389
+ [External Terminal Support](#external-terminal-support)).
390
+
391
+ **Compile-time code (`BEGIN` blocks):** Perl runs `BEGIN` blocks and the
392
+ `use` statements that are themselves `BEGIN` blocks while it is still
393
+ *compiling* your program, before stock `perl5db` ever stops. `tdb` arms the
394
+ debugger ahead of compilation so that code is debuggable too, which means the
395
+ first stop is the first **compile-time** statement of your file (typically
396
+ `use strict;` near the top) rather than the first runtime statement. Step from
397
+ there and you land inside your `BEGIN` blocks, with the stack and evaluate
398
+ views working normally (local variable listing is limited at a compile-time
399
+ stop); the `Stack` view shows the frame as `main::BEGIN`. Stepping through a
400
+ `use` line takes a few steps (the pragma's
401
+ own compile-time work happens in between) but you are never dragged into
402
+ another module's internals.
403
+
404
+ Two consequences worth knowing:
405
+
406
+ - **Breakpoints are deferred while your program compiles.** During the compile
407
+ phase Perl has only parsed part of your file, so its line table is
408
+ incomplete and a breakpoint can't be verified yet. `tdb` holds such requests
409
+ and, while it single-steps through the rest of compilation, checks each
410
+ compile-time statement it lands on against them. A breakpoint placed
411
+ *inside* a `BEGIN` block fires there directly, on the first run, without
412
+ needing to be stepped into by hand. Conditional breakpoints work the same
413
+ way at compile time; a condition that itself errors behaves exactly like a
414
+ bad condition at runtime in that it does not fire. Two residual caveats: a
415
+ breakpoint on a non-statement line (the `BEGIN {` line itself, or a blank
416
+ line) never fires during the compile phase, since it's never actually
417
+ trapped as a statement; and `hitCondition` (break on the Nth hit) isn't
418
+ honored for a compile-time stop, only a plain `condition`.
419
+ - **Startup is slower for large dependency graphs**, because the debugger is
420
+ active throughout compilation. A script with a big `use` tree takes
421
+ noticeably longer to reach its first stop under `tdb`.
422
+
423
+ **`END` blocks** are entered with **step-in** (`s`). `next` and `continue` run
424
+ straight past them to program termination, which is standard `perl5db`
425
+ behavior, not a `tdb` limitation.
426
+
427
+ **When your program dies:** an uncaught Perl error (`die`, or a fatal runtime
428
+ error such as division by zero) opens the same error modal Python tracebacks
429
+ get: the message, the call stack parsed from Perl's `at FILE line N.` /
430
+ `... called at FILE line N` output, and Code View navigated to the failing
431
+ line. This works for compile-time failures too, including a `die` inside a
432
+ `BEGIN` block that aborts compilation. Press `e` in Code View to re-summon the
433
+ last error. `tdb` also reports the debuggee's real exit status rather than
434
+ assuming success.
435
+
325
436
  **Remote attach:** useful when the Perl process is already running (a long-
326
437
  lived service, a process started by something other than `tdb`) or lives on
327
438
  another host/container. Add three lines to the target program, with the
328
439
  `use` line first so the debugger is armed before any of your code compiles:
329
440
 
330
441
  ```perl
331
- use Devel::TdbRemote; # FIRST line of your program
442
+ use Devel::TdbRemote; # first line of your program
332
443
  ...
333
444
  Devel::TdbRemote::listen(5678); # non-blocking
445
+ print "Waiting for tdb to attach on port 5678\n";
334
446
  Devel::TdbRemote::wait_for_client(); # blocks until tdb connects
335
447
  ```
336
448
 
@@ -349,8 +461,8 @@ script controls startup), arm it before Perl even parses your file instead:
349
461
  environment that launches the debuggee.
350
462
 
351
463
  **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:
464
+ script are plain files, not a CPAN install. Copy both onto the remote
465
+ machine and point `PERL5LIB` at the directory that contains them, for example:
354
466
 
355
467
  ```bash
356
468
  # From a checkout or an installed wheel's site-packages/tdb/adapters/perl:
@@ -360,7 +472,7 @@ export PERL5LIB=/opt/tdb-perl:$PERL5LIB
360
472
  ```
361
473
 
362
474
  (`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
475
+ the two files in the same relative layout shown above; `helpers.pl` is a
364
476
  sibling of the `Devel/` directory, not inside it.)
365
477
 
366
478
  **PadWalker (optional but recommended):** inspecting lexical (`my`)
@@ -372,9 +484,70 @@ Install with `cpanm PadWalker` (or your distro's package) for full fidelity.
372
484
 
373
485
  **Pause is unavailable in attach mode.** Launch-mode sessions (`tdb
374
486
  script.pl`) support pausing a running program at any time. Remote-attach
375
- sessions don't — debugpy-style asynchronous pause needs a control channel
487
+ sessions don't. Asynchronous pause (as Python gets via `debugpy`) needs a control channel
376
488
  `Devel::TdbRemote` doesn't implement yet; `pause` in attach mode returns a
377
- clear "not available" error instead of hanging.
489
+ "not available" error instead of hanging.
490
+
491
+ ### Bash
492
+
493
+ `tdb` bundles its own bash adapter (`bash-tdb`) so no separate adapter install
494
+ needed, just a `bash` ≥ 4.4 on `PATH`. It drives stock bash's own `DEBUG`
495
+ trap (with `extdebug` for return-value control) under the hood via a small
496
+ harness script sourced through `BASH_ENV`, so it works with any bash already
497
+ on the system.
498
+
499
+ **Launching a script:**
500
+
501
+ ```bash
502
+ tdb script.sh
503
+ ```
504
+
505
+ Core debugging works as described above (breakpoints, stepping,
506
+ continue/pause, stack, variables, evaluate console), with the v1 caveats
507
+ listed in [What works for non-Python languages](#what-works-for-non-python-languages).
508
+ Most notably, a breakpoint can't be set on a `func() {` line itself (the
509
+ `DEBUG` trap never fires there), stops never land inside subshells or
510
+ pipeline segments, child bash processes run uninstrumented, and only the
511
+ innermost frame's locals are inspectable. There is no remote-attach mode for
512
+ Bash. `--terminal` is supported (see
513
+ [External Terminal Support](#external-terminal-support)).
514
+
515
+ The Variables view shows three scopes for bash: Locals (innermost frame
516
+ only), Globals (unexported shell variables), and Environment (exported
517
+ variables, both inherited and script-`export`ed).
518
+
519
+ ### Tcsh
520
+
521
+ `tdb` bundles its own tcsh adapter (`tcsh-tdb`) so no separate adapter
522
+ is needed, just a stock `tcsh` on `PATH` (or
523
+ `{"adapters": {"tcsh": "/path/to/tcsh"}}` in `config.json`). Stock tcsh has
524
+ no debugger hooks, so the adapter instruments a temporary copy of the script
525
+ (and of any literal `source`d files), runs it with `tcsh -f`, and
526
+ coordinates stops through private FIFOs. tdb always shows the original
527
+ source paths and line numbers; the generated copies are private adapter
528
+ details and are cleaned up when the session ends.
529
+
530
+ **Launching a script:**
531
+
532
+ ```bash
533
+ tdb script.csh
534
+ ```
535
+
536
+ Core debugging works as described above: line breakpoints, stepping
537
+ (`next` steps over `source`d files, `stepIn` steps into instrumented ones),
538
+ stack navigation across `source` nesting, variable inspection, and the
539
+ evaluate console with the v1 caveats listed in
540
+ [What works for non-Python languages](#what-works-for-non-python-languages),
541
+ most notably: no conditional breakpoints, no pause of a free-running
542
+ script, and stepping is per logical source line. `--terminal` is supported
543
+ (see [External Terminal Support](#external-terminal-support)).
544
+
545
+ The Variables view shows four scopes for tcsh: Shell Variables (`set`),
546
+ Environment (`setenv`), Aliases, and Arguments (`argv`). All of them show
547
+ the live state of the single tcsh process. The evaluate console executes
548
+ text directly in the paused shell. It can inspect *and mutate* state
549
+ (`set name = value` takes effect immediately), and a syntax error in
550
+ evaluated text can terminate the debuggee; there is no isolation.
378
551
 
379
552
  ## Layout
380
553
 
@@ -519,8 +692,10 @@ Breakpoints persist across session restarts.
519
692
 
520
693
  ### Variable Inspection
521
694
 
522
- The Variable View shows a tree of scopes (Locals, Globals) with all variables in the current
523
- frame. Expand nodes to drill into complex objects. Children are loaded lazily on demand.
695
+ The Variable View shows a tree of scopes with all variables in the current frame. The scopes
696
+ themselves are language-dependent: Locals, Globals, plus Environment for bash; Lexicals,
697
+ Globals, Specials for Perl (see the [Bash](#bash) and [Perl](#perl) sections above for
698
+ details). Expand nodes to drill into complex objects. Children are loaded lazily on demand.
524
699
  Variable values can be changed in the Evaluate Console.
525
700
 
526
701
  Double-click a variable, or highlight the variable with the text cursor in
@@ -843,22 +1018,82 @@ application directory and a shared library directory) in one invocation.
843
1018
 
844
1019
  ### External Terminal Support
845
1020
 
846
- Some Python programs, notably text user interfaces, use terminal control
847
- codes and require direct access to the terminal to function properly.
848
- Such programs can be debugged with `tdb` by having it launch the debuggee in
849
- a separate terminal:
1021
+ Some programs, notably text user interfaces, use terminal control codes and
1022
+ require direct access to the terminal to function properly. Such programs
1023
+ can be debugged with `tdb` by having it launch the debuggee in a separate
1024
+ terminal:
850
1025
 
851
1026
  ```bash
852
1027
  tdb --terminal xterm my_tui_app.py
853
1028
  ```
854
1029
 
855
- The debuggee runs in a separate window of the specified terminal. Supported choices:
1030
+ `--terminal` works for every language `tdb` can *launch* (as opposed to
1031
+ attach to): Python, Perl, Bash, Tcsh, and C/C++ via `--adapter lldb-dap`.
1032
+ The default C/C++ adapter, `gdb -i dap`, has no terminal integration --
1033
+ `tdb` rejects `--terminal` up front with an error pointing at
1034
+ `--adapter lldb-dap` instead. `--terminal` also only applies when `tdb`
1035
+ launches the program itself; it is rejected for remote-attach (`-r`), since
1036
+ there's no program for `tdb` to spawn a terminal around.
1037
+
1038
+ The debuggee runs in a separate window of the specified terminal, including
1039
+ all of its stdin/stdout/stderr -- keyboard input, program output, and
1040
+ anything the program itself draws to the terminal all happen in that
1041
+ window, not in `tdb`'s Console View. Supported choices:
856
1042
  `xterm`, `konsole`, `gnome-terminal`, `ghostty`, `kitty`, `iterm2`, `warp`,
857
1043
  `wezterm`, `terminator`. The selected terminal must be on `PATH`. Debugging
858
- proceeds as usual in the terminal where `tdb` was invoked.
1044
+ (breakpoints, stepping, variable inspection, the evaluate console) proceeds
1045
+ as usual in the terminal where `tdb` was invoked.
859
1046
 
860
1047
  This feature only works in graphical environments where external terminals are available.
861
1048
 
1049
+ ### Run Mode (`--run`)
1050
+
1051
+ For inspecting a program that appears hung: run it headless, at full speed, with no
1052
+ breakpoints installed and no stop-on-entry, then drop into the full TUI the moment you
1053
+ actually need it -- without paying for either up front.
1054
+
1055
+ ```bash
1056
+ tdb --run my_program.py args...
1057
+ ```
1058
+
1059
+ The debuggee's stdout/stderr stream straight to the terminal. If the program exits on
1060
+ its own, `tdb` exits with the same code:
1061
+
1062
+ ```bash
1063
+ tdb --run my_program.py; echo $? # my_program.py's own exit code
1064
+ ```
1065
+
1066
+ **Interrupting:** press Ctrl-C in the terminal (any platform), or, on Unix, send
1067
+ `SIGUSR1` to `tdb`'s pid from another terminal: `kill -USR1 <tdb pid>`. Either one pauses
1068
+ the debuggee at its currently executing line and opens the TUI with full debugging
1069
+ available -- breakpoints, stepping, variable inspection, the evaluate console. The
1070
+ debuggee itself never receives the signal (`tdb`'s adapter runs in its own process
1071
+ group), so its own `SIGINT` handling is undisturbed.
1072
+
1073
+ **Quitting an adopted session** adds a third choice to the usual quit dialog:
1074
+ - `d` (or `q`) -- detach and resume: the program keeps running headlessly; interrupt it
1075
+ again later the same way.
1076
+ - `t` -- terminate the program and quit `tdb`.
1077
+ - `Esc` -- cancel, stay in the TUI.
1078
+
1079
+ A breakpoint set during one of these episodes stays live after you detach; if it's hit,
1080
+ `tdb` reopens the TUI there, just like an interrupt.
1081
+
1082
+ If the program instead dies on an unhandled exception -- in Python this includes a
1083
+ nonzero `sys.exit()`, which `debugpy` treats as an uncaught `SystemExit` -- `tdb` opens
1084
+ the TUI at the point of failure instead of exiting silently, so you can inspect the
1085
+ crash. Exit-code passthrough applies only to a clean exit during the headless phase.
1086
+
1087
+ Supported languages: Python, Perl, Bash, Tcsh, and C/C++ (both `gdb` and `lldb-dap`).
1088
+ `--run` is rejected up front for any other language.
1089
+
1090
+ **Limitation:** pausing is cooperative. If the debuggee is blocked inside a single
1091
+ blocking external call or syscall, the pause can't land until that call returns --
1092
+ `tdb` prints a notice and opens the TUI once the stop actually arrives.
1093
+
1094
+ **Incompatible flags:** `-r`, `-k`/`-t`, `--record`, `--replay`, `--server`,
1095
+ `--headless`, `--mcp`, `--terminal`.
1096
+
862
1097
  ### Keybinding Schemes
863
1098
 
864
1099
  ```bash
@@ -969,23 +1204,23 @@ Each is JSON with `event`, `data`, and `timestamp` fields.
969
1204
  ## Recording and replaying sessions
970
1205
 
971
1206
  `tdb --record session.jsonl prog.py` runs a normal TUI session and captures
972
- your debugging actions — breakpoints (including `-k`/`-t` and persisted
1207
+ your debugging actions including breakpoints (including `-k`/`-t` and persisted
973
1208
  ones), stepping, continue/pause, Evaluate-console entries, stack-frame
974
- navigation, variable expansion, restart, quit — to `session.jsonl` as
1209
+ navigation, variable expansion, restart, quit. The session is
1210
+ written to `session.jsonl` as
975
1211
  JSON-RPC commands. Works with launch mode (any language) and `-r`
976
1212
  remote attach.
977
1213
 
978
1214
  Replay it two ways:
979
1215
 
980
- - `tdb --replay session.jsonl` — one command: launches the recorded
1216
+ - `tdb --replay session.jsonl` launches the recorded
981
1217
  program headless, feeds every recorded command through the same RPC
982
1218
  dispatch `tdb --server` uses, and prints a transcript (recorded time,
983
1219
  command, verbatim result, interleaved program output). Exit code 0 iff
984
1220
  every command succeeded. Add `--timing` to reproduce the original
985
1221
  pacing, `--replay-timeout S` to bound each stop-wait (default 30 s).
986
1222
  - 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:
1223
+ onward of the file to `POST /rpc` . Each line is a valid request body:
989
1224
 
990
1225
  tail -n +2 session.jsonl | while read line; do
991
1226
  curl -s -X POST -H 'Content-Type: application/json' \
@@ -1039,7 +1274,7 @@ client expects to launch servers.
1039
1274
  | Cluster | Tools |
1040
1275
  |---------|-------|
1041
1276
  | Lifecycle | `debug_launch`, `debug_attach`, `quit` |
1042
- | Control | `control(action, timeout_s=30)` — `action ∈ {continue, next, step_in, step_out, pause, wait_for_stop}` |
1277
+ | Control | `control(action, timeout_s=30)` where `action ∈ {continue, next, step_in, step_out, pause, wait_for_stop}` |
1043
1278
  | Inspection | `inspect(expressions)`, `read_source(file_path)`, `stack_trace()`, `status()`, `get_output()` |
1044
1279
  | Breakpoints | `set_breakpoint(spec, condition?, hit_condition?)`, `remove_breakpoint(spec)`, `list_breakpoints()` |
1045
1280
  | Differentiators | `threads(thread_id?)`, `tasks(task_name?)`, `processes(name_or_pid?)`, `wait_graph()` |
@@ -1109,7 +1344,7 @@ usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
1109
1344
  | `--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. |
1110
1345
  | `--remote-root PATH` | Remote directory matched to `--local-root` (same CLI position via `zip()`). |
1111
1346
  | `-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). |
1347
+ | `-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). |
1113
1348
  | `--no-stop-on-entry` | Do not pause at the first line (default: stop on entry; automatic when `-k` is given) |
1114
1349
  | `--cwd DIR` | Working directory for the debuggee |
1115
1350
  | `--python PATH` | Python interpreter for the debuggee (Python targets only) |
@@ -1144,7 +1379,7 @@ executable path (`{"adapters": {"lldb-dap": "/opt/llvm/bin/lldb-dap"}}`), and
1144
1379
  (`{"default_adapters": {"cpp": "lldb-dap"}}`).
1145
1380
 
1146
1381
  **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
1382
+ found; it's Python code, not an external executable), so
1148
1383
  `adapters.perl` doesn't select an adapter binary. Instead it names the
1149
1384
  *Perl interpreter* tdb should spawn to run the debuggee:
1150
1385
  `{"adapters": {"perl": "/path/to/perl"}}`. Use this when the `perl` on