textual-debugger 0.2.0__tar.gz → 0.2.4__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 (113) hide show
  1. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/PKG-INFO +304 -23
  2. {textual_debugger-0.2.0/src/tdb → textual_debugger-0.2.4}/README.md +297 -18
  3. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/pyproject.toml +7 -5
  4. {textual_debugger-0.2.0 → textual_debugger-0.2.4/src/tdb}/README.md +297 -18
  5. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/__init__.py +1 -1
  6. textual_debugger-0.2.4/src/tdb/adapters/bash/__main__.py +23 -0
  7. textual_debugger-0.2.4/src/tdb/adapters/bash/declares.py +92 -0
  8. textual_debugger-0.2.4/src/tdb/adapters/bash/server.py +360 -0
  9. textual_debugger-0.2.4/src/tdb/adapters/bash/session.py +489 -0
  10. textual_debugger-0.2.4/src/tdb/adapters/bash/tdb_harness.sh +230 -0
  11. textual_debugger-0.2.4/src/tdb/adapters/perl/Devel/TdbCompile.pm +68 -0
  12. textual_debugger-0.2.4/src/tdb/adapters/perl/Devel/TdbRemote.pm +93 -0
  13. textual_debugger-0.2.4/src/tdb/adapters/perl/__main__.py +23 -0
  14. textual_debugger-0.2.4/src/tdb/adapters/perl/helpers.pl +425 -0
  15. textual_debugger-0.2.4/src/tdb/adapters/perl/protocol.py +110 -0
  16. textual_debugger-0.2.4/src/tdb/adapters/perl/refs.py +34 -0
  17. textual_debugger-0.2.4/src/tdb/adapters/perl/server.py +949 -0
  18. textual_debugger-0.2.4/src/tdb/adapters/perl/session.py +317 -0
  19. textual_debugger-0.2.4/src/tdb/adapters/tcsh/__init__.py +7 -0
  20. textual_debugger-0.2.4/src/tdb/adapters/tcsh/__main__.py +3 -0
  21. textual_debugger-0.2.4/src/tdb/adapters/tcsh/breakpoints.py +44 -0
  22. textual_debugger-0.2.4/src/tdb/adapters/tcsh/cli.py +163 -0
  23. textual_debugger-0.2.4/src/tdb/adapters/tcsh/guardian.py +718 -0
  24. textual_debugger-0.2.4/src/tdb/adapters/tcsh/inspect.py +187 -0
  25. textual_debugger-0.2.4/src/tdb/adapters/tcsh/instrumenter.py +354 -0
  26. textual_debugger-0.2.4/src/tdb/adapters/tcsh/models.py +61 -0
  27. textual_debugger-0.2.4/src/tdb/adapters/tcsh/protocol.py +68 -0
  28. textual_debugger-0.2.4/src/tdb/adapters/tcsh/runtime.py +304 -0
  29. textual_debugger-0.2.4/src/tdb/adapters/tcsh/scanner.py +225 -0
  30. textual_debugger-0.2.4/src/tdb/adapters/tcsh/server.py +553 -0
  31. textual_debugger-0.2.4/src/tdb/adapters/tcsh/session.py +1221 -0
  32. textual_debugger-0.2.4/src/tdb/adapters/tcsh/transport.py +914 -0
  33. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/app.py +173 -33
  34. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/app_handlers/dap_events.py +61 -64
  35. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/app_handlers/ui_panels.py +2 -0
  36. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/cli.py +157 -57
  37. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/dap/messages.py +20 -0
  38. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/dap/types.py +22 -0
  39. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/languages/base.py +51 -0
  40. textual_debugger-0.2.4/src/tdb/languages/bash.py +83 -0
  41. textual_debugger-0.2.4/src/tdb/languages/errors.py +235 -0
  42. textual_debugger-0.2.4/src/tdb/languages/perl.py +101 -0
  43. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/languages/python.py +2 -1
  44. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/languages/registry.py +32 -1
  45. textual_debugger-0.2.4/src/tdb/languages/tcsh.py +101 -0
  46. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/mcp/session.py +3 -6
  47. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/persist.py +12 -10
  48. textual_debugger-0.2.4/src/tdb/replay.py +202 -0
  49. textual_debugger-0.2.4/src/tdb/server/__init__.py +0 -0
  50. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/server/event_handler.py +0 -3
  51. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/server/handlers.py +14 -1
  52. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/server/runner.py +53 -18
  53. textual_debugger-0.2.4/src/tdb/session/__init__.py +0 -0
  54. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/session/controller.py +60 -9
  55. textual_debugger-0.2.4/src/tdb/session/recorder.py +106 -0
  56. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/session/state.py +22 -0
  57. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/session/textual_handler.py +0 -1
  58. textual_debugger-0.2.4/src/tdb/widgets/__init__.py +0 -0
  59. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/widgets/async_tasks_modal.py +1 -1
  60. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/widgets/code_view.py +88 -52
  61. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/widgets/evaluate_console.py +2 -3
  62. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/widgets/modals.py +11 -1
  63. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/textual_debugger.egg-info/PKG-INFO +304 -23
  64. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/textual_debugger.egg-info/SOURCES.txt +36 -0
  65. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/textual_debugger.egg-info/requires.txt +1 -1
  66. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/LICENSE +0 -0
  67. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/setup.cfg +0 -0
  68. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/__main__.py +0 -0
  69. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/_timeouts.py +0 -0
  70. {textual_debugger-0.2.0/src/tdb/dap → textual_debugger-0.2.4/src/tdb/adapters}/__init__.py +0 -0
  71. {textual_debugger-0.2.0/src/tdb/server → textual_debugger-0.2.4/src/tdb/adapters/bash}/__init__.py +0 -0
  72. {textual_debugger-0.2.0/src/tdb/session → textual_debugger-0.2.4/src/tdb/adapters/perl}/__init__.py +0 -0
  73. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/app_handlers/__init__.py +0 -0
  74. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/app_handlers/inspection.py +0 -0
  75. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/app_handlers/routing.py +0 -0
  76. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/app_helpers.py +0 -0
  77. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/breakpoint_hook.py +0 -0
  78. {textual_debugger-0.2.0/src/tdb/widgets → textual_debugger-0.2.4/src/tdb/dap}/__init__.py +0 -0
  79. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/dap/client.py +0 -0
  80. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/dap/protocol.py +0 -0
  81. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/inspection.py +0 -0
  82. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/inspection_full.py +0 -0
  83. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/keybindings/__init__.py +0 -0
  84. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/languages/__init__.py +0 -0
  85. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/languages/cpp.py +0 -0
  86. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/mcp/__init__.py +0 -0
  87. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/mcp/__main__.py +0 -0
  88. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/mcp/server.py +0 -0
  89. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/post_mortem.py +0 -0
  90. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/processes_cache.py +0 -0
  91. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/server/app.py +0 -0
  92. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/server/rpc_types.py +0 -0
  93. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/session/child_processes.py +0 -0
  94. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/session/event_bus.py +0 -0
  95. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/session/inspect_service.py +0 -0
  96. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/session/messages.py +0 -0
  97. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/session/post_mortem_loader.py +0 -0
  98. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/session/statement_stepper.py +0 -0
  99. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/session/terminal.py +0 -0
  100. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/source_analysis.py +0 -0
  101. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/widgets/_inspection_modal.py +0 -0
  102. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/widgets/breakpoint_view.py +0 -0
  103. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/widgets/console_view.py +0 -0
  104. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/widgets/full_contents_modal.py +0 -0
  105. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/widgets/menu_bar.py +0 -0
  106. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/widgets/processes_modal.py +0 -0
  107. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/widgets/stack_view.py +0 -0
  108. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/widgets/status_bar.py +0 -0
  109. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/widgets/threads_modal.py +0 -0
  110. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/tdb/widgets/variable_view.py +0 -0
  111. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/textual_debugger.egg-info/dependency_links.txt +0 -0
  112. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/src/textual_debugger.egg-info/entry_points.txt +0 -0
  113. {textual_debugger-0.2.0 → textual_debugger-0.2.4}/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.0
4
- Summary: A TUI Python debugger based on textual and debugpy
3
+ Version: 0.2.4
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
@@ -51,7 +53,7 @@ Requires-Dist: debugpy>=1.8.0
51
53
  Requires-Dist: pygments>=2.17.0
52
54
  Requires-Dist: fastapi>=0.115.0
53
55
  Requires-Dist: uvicorn>=0.30.0
54
- Requires-Dist: mcp>=1.28.0
56
+ Requires-Dist: mcp<2.0.0,>=1.28.0
55
57
  Provides-Extra: dev
56
58
  Requires-Dist: pytest; extra == "dev"
57
59
  Requires-Dist: pytest-cov; extra == "dev"
@@ -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.
@@ -194,6 +202,10 @@ tdb --lang python ./mytool
194
202
  # program runs to the first breakpoint)
195
203
  tdb -k 20 -k 35 -k module.py:14 my_program.py arg1 arg2
196
204
 
205
+ # run straight to line 20 without saving the breakpoint for future
206
+ # sessions (-t is -k minus the persistence)
207
+ tdb -t 20 my_program.py
208
+
197
209
  # use a specific virtualenv
198
210
  tdb --python /path/to/venv/bin/python my_program.py
199
211
 
@@ -232,21 +244,26 @@ python -m tdb my_program.py
232
244
 
233
245
  ## Multi-Language Debugging
234
246
 
235
- `tdb` debugs any language that has a Debug Adapter Protocol backend. Two
247
+ `tdb` debugs any language that has a Debug Adapter Protocol backend. Five
236
248
  languages are supported out of the box:
237
249
 
238
- | Language | Adapter(s) | How to get the adapter | Feature level |
250
+ | Language | Adapter(s) | Dependencies | Feature level |
239
251
  |----------|------------|------------------------|---------------|
240
- | Python | `debugpy` (default) | installed with `textual-debugger` | everything in this README |
252
+ | Python | `debugpy` (default) | Python ≥ 3.11 | everything in this README |
241
253
  | 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 |
254
+ | Perl | perl-tdb (bundled) | perl ≥ 5.18 on PATH | core debugging + remote attach |
255
+ | Bash | bash-tdb (bundled) | bash ≥ 4.4 on PATH | core debugging (no remote attach) |
256
+ | Tcsh | tcsh-tdb (bundled) | tcsh on PATH | core debugging (no remote attach, no conditional breakpoints, no pause) |
242
257
 
243
258
  ### Language detection and selection
244
259
 
245
260
  The language is auto-detected from the debug target:
246
261
 
247
- 1. File extension: `.py` → Python.
262
+ 1. File extension: `.py` → Python; `.pl` / `.pm` / `.t` → Perl; `.sh` / `.bash`
263
+ → Bash; `.csh` / `.tcsh` → Tcsh.
248
264
  2. Native executables (ELF, Mach-O, PE magic bytes) → C/C++.
249
- 3. A `#!...python` shebang → Python.
265
+ 3. A `#!...python`, `#!...perl`, `#!...bash`, or `#!...csh`/`#!...tcsh`
266
+ shebang → Python / Perl / Bash / Tcsh respectively.
250
267
  4. C/C++/Rust *source* files (`.c`, `.cpp`, `.rs`, …) produce an error with a
251
268
  hint: compile with debug info (`g++ -g -O0`) and debug the binary.
252
269
  5. Anything else produces an error naming the `--lang` override.
@@ -278,15 +295,62 @@ conditions and persistence), stepping, continue/pause, run-to-cursor, stack
278
295
  navigation, variable inspection, the evaluate console, syntax highlighting,
279
296
  and the JSON-RPC / MCP programmatic modes.
280
297
 
281
- Python-specific features are hidden or return a clear "not supported for this
298
+ Python-specific features are hidden or return "not supported for this
282
299
  language" message when debugging other languages: statement-granularity
283
300
  stepping (non-Python languages always step per line), the async task /
284
301
  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
302
+ `--python`/`--pv`, `--no-subprocess`, automatic child-process attachment, and
303
+ the post-mortem / `tdb.breakpoint()` hooks (those hooks live inside Python
304
+ programs by nature). Remote attach (`-r`) also works for Perl (see
305
+ [Perl](#perl), `Devel::TdbRemote` in place of `debugpy.listen()`), but not
306
+ for C/C++, Bash, or Tcsh. `--terminal` is
288
307
  currently ignored for non-Python targets.
289
308
 
309
+ **Bash limitations (v1):** the bash adapter uses bash's own `DEBUG` trap and
310
+ has a smaller feature envelope than Python/Perl:
311
+
312
+ - Debuggee code that installs its own `DEBUG` trap clobbers the harness;
313
+ debugging silently degrades to free-running.
314
+ - No stopping inside subshells `(...)`, `$(...)`, or pipeline segments; they
315
+ execute normally.
316
+ - Child bash processes run uninstrumented.
317
+ - Outer-frame locals not inspectable (innermost frame only).
318
+ - Pause is deferred while blocked in an external command.
319
+ - `.sh` files that aren't bash are only diagnosed at launch, by bash itself
320
+ or the harness version check.
321
+ - The `DEBUG` trap never fires on a function-definition line, so a
322
+ breakpoint on a `func() {` line never hits; entry and step-in stops land
323
+ on the first executable line of the function body instead.
324
+ - The debug control channel occupies two inherited file descriptors
325
+ (typically high-numbered). A script that execs redirections onto those
326
+ exact fds (e.g. `exec 63>&-` or reusing them for its own I/O) will
327
+ silently break debugging.
328
+
329
+ See [Bash](#bash) below for launch details.
330
+
331
+ **Tcsh limitations (v1):** tcsh has no debug hooks at all, so the tcsh
332
+ adapter debugs an instrumented temporary *copy* of the script (original
333
+ paths and line numbers are preserved in everything tdb displays):
334
+
335
+ - Conditional breakpoints are not supported; a condition set in the
336
+ Breakpoint View is ignored for tcsh (the breakpoint always stops).
337
+ - No asynchronous pause: a free-running script stops only at the next
338
+ breakpoint (or when it exits).
339
+ - A breakpoint binds to the nearest safe statement at or after the
340
+ requested line; unplaceable breakpoints are reported unverified.
341
+ - Stack frames represent `source`d files, not native call frames, and only
342
+ literal `source` targets resolvable at launch are instrumented (computed
343
+ or `cd`-dependent sources run normally but are atomic to the debugger).
344
+ - All frames show the same live shell state (Shell Variables, Environment,
345
+ Aliases, Arguments). Stock tcsh keeps no per-frame history.
346
+ - Multiple commands on one physical line (`a ; b`), command substitutions,
347
+ and external commands are atomic stepping units.
348
+ - `$0` inside dynamically generated or evaluated text can expose the
349
+ generated copy's path (ordinary lexical `$0` is rewritten correctly).
350
+ - Requires Python ≥ 3.11.
351
+
352
+ See [Tcsh](#tcsh) below for launch details.
353
+
290
354
  ### C/C++ tips
291
355
 
292
356
  - Compile with `-g` (ideally `-g -O0`). If no breakpoint in a file can be
@@ -297,12 +361,183 @@ currently ignored for non-Python targets.
297
361
  variables, and evaluate console remain fully usable.
298
362
  - GDB (the default adapter) has the most complete libstdc++
299
363
  pretty-printing. `lldb-dap` (via `--adapter lldb-dap`) also debugs
300
- GCC-built binaries fine — DWARF is compiler-neutral.
364
+ GCC-built binaries fine. DWARF is compiler-neutral.
301
365
  - **GDB evaluate-console quirk:** GDB's DAP treats REPL input as CLI
302
366
  commands, so evaluate expressions with an explicit `print`, e.g.
303
367
  `print x` rather than bare `x` (bare `x` collides with GDB's
304
368
  examine-memory command). `lldb-dap` evaluates bare expressions directly.
305
369
 
370
+ ### Perl
371
+
372
+ `tdb` bundles its own Perl adapter (`perl-tdb`) so only need
373
+ `perl` ≥ 5.18 on `PATH`. It drives stock `perl5db` under the
374
+ hood, so it works with any Perl already on the system.
375
+
376
+ **Launching a script:**
377
+
378
+ ```bash
379
+ tdb script.pl
380
+ ```
381
+
382
+ **Compile-time code (`BEGIN` blocks):** Perl runs `BEGIN` blocks and the
383
+ `use` statements that are themselves `BEGIN` blocks while it is still
384
+ *compiling* your program, before stock `perl5db` ever stops. `tdb` arms the
385
+ debugger ahead of compilation so that code is debuggable too, which means the
386
+ first stop is the first **compile-time** statement of your file (typically
387
+ `use strict;` near the top) rather than the first runtime statement. Step from
388
+ there and you land inside your `BEGIN` blocks, with the stack and evaluate
389
+ views working normally (local variable listing is limited at a compile-time
390
+ stop); the `Stack` view shows the frame as `main::BEGIN`. Stepping through a
391
+ `use` line takes a few steps (the pragma's
392
+ own compile-time work happens in between) but you are never dragged into
393
+ another module's internals.
394
+
395
+ Two consequences worth knowing:
396
+
397
+ - **Breakpoints are deferred while your program compiles.** During the compile
398
+ phase Perl has only parsed part of your file, so its line table is
399
+ incomplete and a breakpoint can't be verified yet. `tdb` holds such requests
400
+ and, while it single-steps through the rest of compilation, checks each
401
+ compile-time statement it lands on against them. A breakpoint placed
402
+ *inside* a `BEGIN` block fires there directly, on the first run, without
403
+ needing to be stepped into by hand. Conditional breakpoints work the same
404
+ way at compile time; a condition that itself errors behaves exactly like a
405
+ bad condition at runtime in that it does not fire. Two residual caveats: a
406
+ breakpoint on a non-statement line (the `BEGIN {` line itself, or a blank
407
+ line) never fires during the compile phase, since it's never actually
408
+ trapped as a statement; and `hitCondition` (break on the Nth hit) isn't
409
+ honored for a compile-time stop, only a plain `condition`.
410
+ - **Startup is slower for large dependency graphs**, because the debugger is
411
+ active throughout compilation. A script with a big `use` tree takes
412
+ noticeably longer to reach its first stop under `tdb`.
413
+
414
+ **`END` blocks** are entered with **step-in** (`s`). `next` and `continue` run
415
+ straight past them to program termination, which is standard `perl5db`
416
+ behavior, not a `tdb` limitation.
417
+
418
+ **When your program dies:** an uncaught Perl error (`die`, or a fatal runtime
419
+ error such as division by zero) opens the same error modal Python tracebacks
420
+ get: the message, the call stack parsed from Perl's `at FILE line N.` /
421
+ `... called at FILE line N` output, and Code View navigated to the failing
422
+ line. This works for compile-time failures too, including a `die` inside a
423
+ `BEGIN` block that aborts compilation. Press `e` in Code View to re-summon the
424
+ last error. `tdb` also reports the debuggee's real exit status rather than
425
+ assuming success.
426
+
427
+ **Remote attach:** useful when the Perl process is already running (a long-
428
+ lived service, a process started by something other than `tdb`) or lives on
429
+ another host/container. Add three lines to the target program, with the
430
+ `use` line first so the debugger is armed before any of your code compiles:
431
+
432
+ ```perl
433
+ use Devel::TdbRemote; # first line of your program
434
+ ...
435
+ Devel::TdbRemote::listen(5678); # non-blocking
436
+ print "Waiting for tdb to attach on port 5678\n";
437
+ Devel::TdbRemote::wait_for_client(); # blocks until tdb connects
438
+ ```
439
+
440
+ Then attach from `tdb`, forcing the language since there's no local `program`
441
+ argument for `tdb` to detect it from:
442
+
443
+ ```bash
444
+ tdb --lang perl -r host:5678
445
+ ```
446
+
447
+ **Arming caveat:** only code *compiled after* the debugger is armed can be
448
+ stepped into or breakpointed. That's why `use Devel::TdbRemote;` must be the
449
+ first line of the program. If you can't edit the first line (e.g. a wrapper
450
+ script controls startup), arm it before Perl even parses your file instead:
451
+ `perl -d:TdbRemote prog.pl`, or set `PERL5OPT=-d:TdbRemote` in the
452
+ environment that launches the debuggee.
453
+
454
+ **Copying the adapter to a remote host:** `Devel::TdbRemote` and its helper
455
+ script are plain files, not a CPAN install. Copy both onto the remote
456
+ machine and point `PERL5LIB` at the directory that contains them, for example:
457
+
458
+ ```bash
459
+ # From a checkout or an installed wheel's site-packages/tdb/adapters/perl:
460
+ scp -r Devel/TdbRemote.pm helpers.pl remote-host:/opt/tdb-perl/
461
+ # On the remote host:
462
+ export PERL5LIB=/opt/tdb-perl:$PERL5LIB
463
+ ```
464
+
465
+ (`Devel/TdbRemote.pm` locates `helpers.pl` next to itself at runtime, so keep
466
+ the two files in the same relative layout shown above; `helpers.pl` is a
467
+ sibling of the `Devel/` directory, not inside it.)
468
+
469
+ **PadWalker (optional but recommended):** inspecting lexical (`my`)
470
+ variables in the *current* frame always works. Lexicals in outer/caller
471
+ frames need the `PadWalker` module installed on the debuggee's Perl; without
472
+ it, tdb falls back to a read-only pad walk that can't reach fully into
473
+ enclosing scopes, and outer-frame variable listings degrade accordingly.
474
+ Install with `cpanm PadWalker` (or your distro's package) for full fidelity.
475
+
476
+ **Pause is unavailable in attach mode.** Launch-mode sessions (`tdb
477
+ script.pl`) support pausing a running program at any time. Remote-attach
478
+ sessions don't. Asynchronous pause (as Python gets via `debugpy`) needs a control channel
479
+ `Devel::TdbRemote` doesn't implement yet; `pause` in attach mode returns a
480
+ "not available" error instead of hanging.
481
+
482
+ ### Bash
483
+
484
+ `tdb` bundles its own bash adapter (`bash-tdb`) so no separate adapter install
485
+ needed, just a `bash` ≥ 4.4 on `PATH`. It drives stock bash's own `DEBUG`
486
+ trap (with `extdebug` for return-value control) under the hood via a small
487
+ harness script sourced through `BASH_ENV`, so it works with any bash already
488
+ on the system.
489
+
490
+ **Launching a script:**
491
+
492
+ ```bash
493
+ tdb script.sh
494
+ ```
495
+
496
+ Core debugging works as described above (breakpoints, stepping,
497
+ continue/pause, stack, variables, evaluate console), with the v1 caveats
498
+ listed in [What works for non-Python languages](#what-works-for-non-python-languages).
499
+ Most notably, a breakpoint can't be set on a `func() {` line itself (the
500
+ `DEBUG` trap never fires there), stops never land inside subshells or
501
+ pipeline segments, child bash processes run uninstrumented, and only the
502
+ innermost frame's locals are inspectable. There is no remote-attach mode for
503
+ Bash.
504
+
505
+ The Variables view shows three scopes for bash: Locals (innermost frame
506
+ only), Globals (unexported shell variables), and Environment (exported
507
+ variables, both inherited and script-`export`ed).
508
+
509
+ ### Tcsh
510
+
511
+ `tdb` bundles its own tcsh adapter (`tcsh-tdb`) so no separate adapter
512
+ is needed, just a stock `tcsh` on `PATH` (or
513
+ `{"adapters": {"tcsh": "/path/to/tcsh"}}` in `config.json`). Stock tcsh has
514
+ no debugger hooks, so the adapter instruments a temporary copy of the script
515
+ (and of any literal `source`d files), runs it with `tcsh -f`, and
516
+ coordinates stops through private FIFOs. tdb always shows the original
517
+ source paths and line numbers; the generated copies are private adapter
518
+ details and are cleaned up when the session ends.
519
+
520
+ **Launching a script:**
521
+
522
+ ```bash
523
+ tdb script.csh
524
+ ```
525
+
526
+ Core debugging works as described above: line breakpoints, stepping
527
+ (`next` steps over `source`d files, `stepIn` steps into instrumented ones),
528
+ stack navigation across `source` nesting, variable inspection, and the
529
+ evaluate console with the v1 caveats listed in
530
+ [What works for non-Python languages](#what-works-for-non-python-languages),
531
+ most notably: no conditional breakpoints, no pause of a free-running
532
+ script, and stepping is per logical source line.
533
+
534
+ The Variables view shows four scopes for tcsh: Shell Variables (`set`),
535
+ Environment (`setenv`), Aliases, and Arguments (`argv`). All of them show
536
+ the live state of the single tcsh process. The evaluate console executes
537
+ text directly in the paused shell. It can inspect *and mutate* state
538
+ (`set name = value` takes effect immediately), and a syntax error in
539
+ evaluated text can terminate the debuggee; there is no isolation.
540
+
306
541
  ## Layout
307
542
 
308
543
  ```
@@ -446,8 +681,10 @@ Breakpoints persist across session restarts.
446
681
 
447
682
  ### Variable Inspection
448
683
 
449
- The Variable View shows a tree of scopes (Locals, Globals) with all variables in the current
450
- frame. Expand nodes to drill into complex objects. Children are loaded lazily on demand.
684
+ The Variable View shows a tree of scopes with all variables in the current frame. The scopes
685
+ themselves are language-dependent: Locals, Globals, plus Environment for bash; Lexicals,
686
+ Globals, Specials for Perl (see the [Bash](#bash) and [Perl](#perl) sections above for
687
+ details). Expand nodes to drill into complex objects. Children are loaded lazily on demand.
451
688
  Variable values can be changed in the Evaluate Console.
452
689
 
453
690
  Double-click a variable, or highlight the variable with the text cursor in
@@ -893,6 +1130,40 @@ curl -N http://127.0.0.1:8150/events
893
1130
  Events: `initialized`, `stopped`, `continued`, `terminated`, `exited`, `output`.
894
1131
  Each is JSON with `event`, `data`, and `timestamp` fields.
895
1132
 
1133
+ ## Recording and replaying sessions
1134
+
1135
+ `tdb --record session.jsonl prog.py` runs a normal TUI session and captures
1136
+ your debugging actions including breakpoints (including `-k`/`-t` and persisted
1137
+ ones), stepping, continue/pause, Evaluate-console entries, stack-frame
1138
+ navigation, variable expansion, restart, quit. The session is
1139
+ written to `session.jsonl` as
1140
+ JSON-RPC commands. Works with launch mode (any language) and `-r`
1141
+ remote attach.
1142
+
1143
+ Replay it two ways:
1144
+
1145
+ - `tdb --replay session.jsonl` launches the recorded
1146
+ program headless, feeds every recorded command through the same RPC
1147
+ dispatch `tdb --server` uses, and prints a transcript (recorded time,
1148
+ command, verbatim result, interleaved program output). Exit code 0 iff
1149
+ every command succeeded. Add `--timing` to reproduce the original
1150
+ pacing, `--replay-timeout S` to bound each stop-wait (default 30 s).
1151
+ - Against a live server: start `tdb --server prog.py`, then feed line 2
1152
+ onward of the file to `POST /rpc` . Each line is a valid request body:
1153
+
1154
+ tail -n +2 session.jsonl | while read line; do
1155
+ curl -s -X POST -H 'Content-Type: application/json' \
1156
+ -d "$line" http://127.0.0.1:8150/rpc
1157
+ done
1158
+
1159
+ (On Windows, an equivalent loop in Python: read the file, skip the
1160
+ first line, `requests.post` each remaining line.)
1161
+
1162
+ Not captured: pure viewing (scrolling, search, modals, thread/task
1163
+ lists), breakpoint enable/disable toggles, variable expansions when the
1164
+ adapter reports no `evaluateName` (currently the Perl adapter), and
1165
+ File > Open program switches.
1166
+
896
1167
  ## MCP Integration
897
1168
 
898
1169
  tdb ships a Model Context Protocol (MCP) server (`tdb-mcp`) that exposes
@@ -932,7 +1203,7 @@ client expects to launch servers.
932
1203
  | Cluster | Tools |
933
1204
  |---------|-------|
934
1205
  | Lifecycle | `debug_launch`, `debug_attach`, `quit` |
935
- | Control | `control(action, timeout_s=30)` — `action ∈ {continue, next, step_in, step_out, pause, wait_for_stop}` |
1206
+ | Control | `control(action, timeout_s=30)` where `action ∈ {continue, next, step_in, step_out, pause, wait_for_stop}` |
936
1207
  | Inspection | `inspect(expressions)`, `read_source(file_path)`, `stack_trace()`, `status()`, `get_output()` |
937
1208
  | Breakpoints | `set_breakpoint(spec, condition?, hit_condition?)`, `remove_breakpoint(spec)`, `list_breakpoints()` |
938
1209
  | Differentiators | `threads(thread_id?)`, `tasks(task_name?)`, `processes(name_or_pid?)`, `wait_graph()` |
@@ -1002,11 +1273,12 @@ usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
1002
1273
  | `--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. |
1003
1274
  | `--remote-root PATH` | Remote directory matched to `--local-root` (same CLI position via `zip()`). |
1004
1275
  | `-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. |
1276
+ | `-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). |
1005
1277
  | `--no-stop-on-entry` | Do not pause at the first line (default: stop on entry; automatic when `-k` is given) |
1006
1278
  | `--cwd DIR` | Working directory for the debuggee |
1007
1279
  | `--python PATH` | Python interpreter for the debuggee (Python targets only) |
1008
1280
  | `--pv` | Shorthand for --python .venv/bin/python |
1009
- | `--lang LANGUAGE` | Debuggee language (`python`, `cpp`); default: auto-detect from the target |
1281
+ | `--lang LANGUAGE` | Debuggee language (`python`, `cpp`, `perl`); default: auto-detect from the target |
1010
1282
  | `--adapter ADAPTER` | Debug adapter within the language (e.g. `--lang cpp --adapter lldb-dap`); default: the language's standard adapter |
1011
1283
  | `--no-just-my-code` | Step into stdlib/site-packages code instead of skipping it
1012
1284
  (default: skipped). On uncaught exceptions, the crash modal always shows the full traceback
@@ -1035,8 +1307,17 @@ executable path (`{"adapters": {"lldb-dap": "/opt/llvm/bin/lldb-dap"}}`), and
1035
1307
  `default_adapters` picks a language's default adapter
1036
1308
  (`{"default_adapters": {"cpp": "lldb-dap"}}`).
1037
1309
 
1310
+ **Perl is a special case:** `perl-tdb` is tdb's own bundled adapter (always
1311
+ found; it's Python code, not an external executable), so
1312
+ `adapters.perl` doesn't select an adapter binary. Instead it names the
1313
+ *Perl interpreter* tdb should spawn to run the debuggee:
1314
+ `{"adapters": {"perl": "/path/to/perl"}}`. Use this when the `perl` on
1315
+ `PATH` is too old (< 5.18) or you need a specific `perlbrew`/`plenv` version.
1316
+
1038
1317
  Breakpoints are saved on exit and restored when debugging a program in the same
1039
- directory. Each project's breakpoints are independent.
1318
+ directory. Each project's breakpoints are independent. Breakpoints set with
1319
+ `-t`/`--to-line` are the exception: they behave like `-k` breakpoints during
1320
+ the session but are never saved.
1040
1321
 
1041
1322
  **Step mode** (`step_mode` in `config.json`) controls how `n` (step over) and `s` (step
1042
1323
  into) handle multi-line source statements: