textual-debugger 0.2.6__tar.gz → 0.2.8__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 (136) hide show
  1. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/PKG-INFO +294 -43
  2. {textual_debugger-0.2.6/src/tdb → textual_debugger-0.2.8}/README.md +293 -42
  3. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/pyproject.toml +2 -1
  4. {textual_debugger-0.2.6 → textual_debugger-0.2.8/src/tdb}/README.md +293 -42
  5. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/__init__.py +1 -1
  6. textual_debugger-0.2.8/src/tdb/adapters/ocaml/lldb_formatters.py +153 -0
  7. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/perl/server.py +21 -10
  8. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/perl/session.py +9 -1
  9. textual_debugger-0.2.8/src/tdb/adapters/ruby/__init__.py +1 -0
  10. textual_debugger-0.2.8/src/tdb/adapters/ruby/__main__.py +23 -0
  11. textual_debugger-0.2.8/src/tdb/adapters/ruby/server.py +1120 -0
  12. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/app.py +28 -17
  13. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/app_handlers/dap_events.py +3 -0
  14. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/app_handlers/inspection.py +101 -1
  15. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/app_handlers/routing.py +36 -0
  16. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/app_handlers/ui_panels.py +39 -0
  17. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/cli.py +41 -10
  18. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/dap/client.py +3 -0
  19. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/languages/base.py +63 -1
  20. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/languages/bash.py +3 -1
  21. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/languages/cpp.py +49 -6
  22. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/languages/errors.py +216 -0
  23. textual_debugger-0.2.8/src/tdb/languages/ocaml.py +324 -0
  24. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/languages/perl.py +3 -1
  25. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/languages/python.py +6 -4
  26. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/languages/registry.py +71 -9
  27. textual_debugger-0.2.8/src/tdb/languages/ruby.py +117 -0
  28. textual_debugger-0.2.8/src/tdb/languages/rust.py +158 -0
  29. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/languages/tcsh.py +3 -1
  30. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/mcp/server.py +41 -9
  31. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/mcp/session.py +29 -5
  32. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/replay.py +12 -2
  33. textual_debugger-0.2.8/src/tdb/rust_concurrency/__init__.py +41 -0
  34. textual_debugger-0.2.8/src/tdb/rust_concurrency/analyzer.py +505 -0
  35. textual_debugger-0.2.8/src/tdb/rust_concurrency/classifier.py +255 -0
  36. textual_debugger-0.2.8/src/tdb/rust_concurrency/collector.py +337 -0
  37. textual_debugger-0.2.8/src/tdb/rust_concurrency/models.py +269 -0
  38. textual_debugger-0.2.8/src/tdb/rust_concurrency/probes/__init__.py +18 -0
  39. textual_debugger-0.2.8/src/tdb/rust_concurrency/probes/base.py +166 -0
  40. textual_debugger-0.2.8/src/tdb/rust_concurrency/probes/gdb.py +27 -0
  41. textual_debugger-0.2.8/src/tdb/rust_concurrency/probes/gdb_script.py +238 -0
  42. textual_debugger-0.2.8/src/tdb/rust_concurrency/probes/lldb.py +35 -0
  43. textual_debugger-0.2.8/src/tdb/rust_concurrency/probes/lldb_script.py +276 -0
  44. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/server/app.py +1 -1
  45. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/server/handlers.py +21 -0
  46. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/server/rpc_types.py +17 -0
  47. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/server/runner.py +15 -3
  48. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/session/controller.py +112 -14
  49. textual_debugger-0.2.8/src/tdb/session/errors.py +23 -0
  50. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/session/inspect_service.py +24 -15
  51. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/session/recorder.py +3 -0
  52. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/session/state.py +2 -0
  53. textual_debugger-0.2.8/src/tdb/widgets/__init__.py +0 -0
  54. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/widgets/_inspection_modal.py +11 -2
  55. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/widgets/modals.py +21 -10
  56. textual_debugger-0.2.8/src/tdb/widgets/rust_concurrency_modal.py +397 -0
  57. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/widgets/stack_view.py +6 -2
  58. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/widgets/threads_modal.py +55 -8
  59. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/textual_debugger.egg-info/PKG-INFO +294 -43
  60. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/textual_debugger.egg-info/SOURCES.txt +21 -0
  61. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/LICENSE +0 -0
  62. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/setup.cfg +0 -0
  63. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/__main__.py +0 -0
  64. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/_timeouts.py +0 -0
  65. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/__init__.py +0 -0
  66. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/bash/__init__.py +0 -0
  67. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/bash/__main__.py +0 -0
  68. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/bash/declares.py +0 -0
  69. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/bash/server.py +0 -0
  70. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/bash/session.py +0 -0
  71. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/bash/tdb_harness.sh +0 -0
  72. {textual_debugger-0.2.6/src/tdb/adapters/perl → textual_debugger-0.2.8/src/tdb/adapters/ocaml}/__init__.py +0 -0
  73. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/perl/Devel/TdbCompile.pm +0 -0
  74. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/perl/Devel/TdbRemote.pm +0 -0
  75. {textual_debugger-0.2.6/src/tdb/dap → textual_debugger-0.2.8/src/tdb/adapters/perl}/__init__.py +0 -0
  76. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/perl/__main__.py +0 -0
  77. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/perl/helpers.pl +0 -0
  78. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/perl/protocol.py +0 -0
  79. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/perl/refs.py +0 -0
  80. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/tcsh/__init__.py +0 -0
  81. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/tcsh/__main__.py +0 -0
  82. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/tcsh/breakpoints.py +0 -0
  83. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/tcsh/cli.py +0 -0
  84. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/tcsh/guardian.py +0 -0
  85. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/tcsh/inspect.py +0 -0
  86. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/tcsh/instrumenter.py +0 -0
  87. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/tcsh/models.py +0 -0
  88. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/tcsh/protocol.py +0 -0
  89. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/tcsh/runtime.py +0 -0
  90. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/tcsh/scanner.py +0 -0
  91. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/tcsh/server.py +0 -0
  92. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/tcsh/session.py +0 -0
  93. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/adapters/tcsh/transport.py +0 -0
  94. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/app_handlers/__init__.py +0 -0
  95. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/app_helpers.py +0 -0
  96. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/breakpoint_hook.py +0 -0
  97. {textual_debugger-0.2.6/src/tdb/server → textual_debugger-0.2.8/src/tdb/dap}/__init__.py +0 -0
  98. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/dap/messages.py +0 -0
  99. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/dap/protocol.py +0 -0
  100. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/dap/reverse.py +0 -0
  101. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/dap/types.py +0 -0
  102. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/inspection.py +0 -0
  103. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/inspection_full.py +0 -0
  104. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/keybindings/__init__.py +0 -0
  105. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/languages/__init__.py +0 -0
  106. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/mcp/__init__.py +0 -0
  107. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/mcp/__main__.py +0 -0
  108. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/persist.py +0 -0
  109. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/post_mortem.py +0 -0
  110. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/processes_cache.py +0 -0
  111. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/run_mode.py +0 -0
  112. {textual_debugger-0.2.6/src/tdb/session → textual_debugger-0.2.8/src/tdb/server}/__init__.py +0 -0
  113. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/server/event_handler.py +0 -0
  114. {textual_debugger-0.2.6/src/tdb/widgets → textual_debugger-0.2.8/src/tdb/session}/__init__.py +0 -0
  115. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/session/child_processes.py +0 -0
  116. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/session/event_bus.py +0 -0
  117. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/session/messages.py +0 -0
  118. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/session/post_mortem_loader.py +0 -0
  119. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/session/statement_stepper.py +0 -0
  120. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/session/terminal.py +0 -0
  121. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/session/textual_handler.py +0 -0
  122. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/source_analysis.py +0 -0
  123. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/widgets/async_tasks_modal.py +0 -0
  124. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/widgets/breakpoint_view.py +0 -0
  125. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/widgets/code_view.py +0 -0
  126. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/widgets/console_view.py +0 -0
  127. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/widgets/evaluate_console.py +0 -0
  128. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/widgets/full_contents_modal.py +0 -0
  129. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/widgets/menu_bar.py +0 -0
  130. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/widgets/processes_modal.py +0 -0
  131. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/widgets/status_bar.py +0 -0
  132. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/tdb/widgets/variable_view.py +0 -0
  133. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/textual_debugger.egg-info/dependency_links.txt +0 -0
  134. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/textual_debugger.egg-info/entry_points.txt +0 -0
  135. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/src/textual_debugger.egg-info/requires.txt +0 -0
  136. {textual_debugger-0.2.6 → textual_debugger-0.2.8}/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.2.6
3
+ Version: 0.2.8
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:
@@ -73,16 +73,16 @@ a full-featured terminal-based debugger for Python and other languages
73
73
  with a Debug Adapter Protocol (DAP) implementation. In addition to Python,
74
74
  `tdb` comes with built-in support for
75
75
  - C and C++ (via `gdb` or `lldb-dap`)
76
+ - Rust (via `gdb` or `lldb-dap`, selected explicitly)
76
77
  - Perl (via `perl -d`)
77
78
  - Bash (via bash's own `DEBUG` trap; bash ≥ 4.4)
78
79
  - Tcsh (via source instrumentation of a stock `tcsh`)
80
+ - Ruby (via the [debug gem](https://github.com/ruby/debug)'s `rdbg`; debug ≥ 1.9)
81
+ - OCaml (native via `lldb-dap`/`gdb`, bytecode via [ocamlearlybird](https://github.com/hackwaly/ocamlearlybird))
79
82
 
80
83
  `tdb` is built with [textual](https://github.com/Textualize/textual) and speaks
81
- DAP to a pluggable debug adapter: [debugpy](https://github.com/microsoft/debugpy)
82
- (the engine behind VS Code's Python debugger) for Python,
83
- [GDB's DAP mode](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Debugger-Adapter-Protocol.html) or
84
- [lldb-dap](https://lldb.llvm.org/resources/lldbdap.html)
85
- for compiled code. It provides a rich interactive interface for stepping through
84
+ DAP to a pluggable debug adapter.
85
+ It provides a rich interactive interface for stepping through
86
86
  code, inspecting variables, managing breakpoints, and evaluating expressions in
87
87
  complex programs.
88
88
 
@@ -96,10 +96,13 @@ MIT License. Copyright 2026 by Al Danial.
96
96
  `tdb`:
97
97
 
98
98
  - debugs multiple languages through the Debug Adapter Protocol: Python (via `debugpy`,
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
102
- auto-detected from the target (ref. [Multi-Language Debugging](#multi-language-debugging)).
99
+ the richest feature set), C/C++ (via `gdb -i dap` or `lldb-dap`), Rust (via `gdb` or
100
+ `lldb-dap`), Perl (via `perl -d`),
101
+ Bash (via bash's own `DEBUG` trap), Tcsh (via source instrumentation of a
102
+ stock `tcsh`), Ruby (via the debug gem's `rdbg`), and OCaml (native via
103
+ `lldb-dap`/`gdb`, bytecode via ocamlearlybird). Most languages are
104
+ auto-detected from the target; Rust intentionally requires `--lang rust`
105
+ (ref. [Multi-Language Debugging](#multi-language-debugging)).
103
106
 
104
107
  - supports debugging of synchronous, asynchronous, multi-threaded, and multi-process Python code.
105
108
  It specifically supports modules
@@ -107,7 +110,7 @@ It specifically supports modules
107
110
  - `threading` (with a thread inspector)
108
111
  - `multiprocessing` / `concurrent.futures` (with automatic child process attachment and a process inspector)
109
112
 
110
- - supports remote attachment to debugpy-enabled Python programs
113
+ - supports remote attachment to Python, Perl, Ruby, Rust, and C/C++ programs
111
114
 
112
115
  - includes a JSON-RPC server mode, an MCP mode, and a `SKILL.md` file that enable
113
116
  programmatic debug control, making it suitable for
@@ -142,7 +145,8 @@ as open source.
142
145
  - OpenAI, for providing access to Codex through the
143
146
  [Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
144
147
 
145
- This project was inspired by Andreas Klöckner's excellent [pudb](https://pypi.org/project/pudb/) debugger.
148
+ This project was inspired by Andreas Klöckner's excellent [pudb](https://pypi.org/project/pudb/)
149
+ Python debugger.
146
150
 
147
151
  ## Gallery
148
152
  <p align="center">
@@ -246,32 +250,49 @@ python -m tdb my_program.py
246
250
 
247
251
  ## Multi-Language Debugging
248
252
 
249
- `tdb` debugs any language that has a Debug Adapter Protocol backend. Five
253
+ `tdb` debugs any language that has a Debug Adapter Protocol backend. Eight
250
254
  languages are supported out of the box:
251
255
 
252
256
  | Language | Adapter(s) | Dependencies | Feature level |
253
257
  |----------|------------|------------------------|---------------|
254
258
  | Python | `debugpy` (default) | Python ≥ 3.11 | everything in this README |
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 |
259
+ | 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 + remote attach (gdbserver/lldb-server, local symbol-bearing executable required) |
260
+ | Rust (explicit `--lang rust`) | `gdb` (Linux default), `lldb-dap` (macOS default) | any rustc building with debug info; GDB ≥ 14 or LLVM `lldb-dap` ≥ 17 (concurrency ownership evidence additionally requires stable Rust 1.98) | core debugging + best-effort Rust concurrency inspection + remote attach |
256
261
  | Perl | perl-tdb (bundled) | perl ≥ 5.18 on PATH | core debugging + remote attach |
257
262
  | Bash | bash-tdb (bundled) | bash ≥ 4.4 on PATH | core debugging (no remote attach) |
258
263
  | Tcsh | tcsh-tdb (bundled) | tcsh on PATH | core debugging (no remote attach, no conditional breakpoints, no pause) |
264
+ | Ruby | `rdbg` (the [debug gem](https://github.com/ruby/debug)) | `rdbg` ≥ 1.9 on PATH | core debugging + remote attach |
265
+ | OCaml (native executable) | `lldb-dap` (default), `gdb` (alternate) | OCaml ≥ 4.12; `lldb-dap` ships with LLVM ≥ 17; `gdb -i dap` requires GDB ≥ 14 | core debugging + domains-as-threads; Variables view shows no OCaml locals (upstream DWARF limitation); evaluate console is lldb/C-level, not OCaml |
266
+ | OCaml (bytecode executable) | `ocamlearlybird` (default) | OCaml ≥ 4.12; `opam install earlybird` | core debugging + rich OCaml locals; no pause/`--run`, no evaluate responses, no fatal-error modal (ocamlearlybird 1.3.6 limitations); single-domain only |
259
267
 
260
268
  ### Language detection and selection
261
269
 
262
270
  The language is auto-detected from the debug target:
263
271
 
264
272
  1. File extension: `.py` → Python; `.pl` / `.pm` / `.t` → Perl; `.sh` / `.bash`
265
- → Bash; `.csh` / `.tcsh` → Tcsh.
266
- 2. Native executables (ELF, Mach-O, PE magic bytes) → C/C++.
267
- 3. A `#!...python`, `#!...perl`, `#!...bash`, or `#!...csh`/`#!...tcsh`
268
- shebang → Python / Perl / Bash / Tcsh respectively.
273
+ → Bash; `.csh` / `.tcsh` → Tcsh; `.rb` → Ruby. (`.ml` / `.mli` do *not*
274
+ auto-select OCaml, see point 5.)
275
+ 2. Native executables (ELF, Mach-O, PE magic bytes) → C/C++, unless
276
+ byte-sniffing finds an OCaml marker, either a native binary's
277
+ `caml_program`/`caml_startup` runtime symbols, a bytecode file's
278
+ trailing `Caml1999` marker, or a `#!...ocamlrun` shebang, in which case
279
+ → OCaml (native or bytecode respectively). A stripped native OCaml
280
+ binary that byte-sniffing can't identify falls back to C/C++; force it
281
+ with `--lang ocaml`. Rust is never inferred from an executable; select
282
+ it explicitly with `--lang rust`.
283
+ 3. A `#!...python`, `#!...perl`, `#!...bash`, `#!...csh`/`#!...tcsh`, or
284
+ `#!...ruby` shebang → Python / Perl / Bash / Tcsh / Ruby respectively.
269
285
  4. C/C++/Rust *source* files (`.c`, `.cpp`, `.rs`, …) produce an error with a
270
286
  hint: compile with debug info (`g++ -g -O0`) and debug the binary.
271
- 5. Anything else produces an error naming the `--lang` override.
287
+ 5. `.ml` / `.mli` (OCaml source) produce an error too: build the project
288
+ first (dune's dev profile keeps debug info) and pass the **built
289
+ executable** to `tdb` (`tdb ./_build/default/bin/main.exe`), never the
290
+ `.ml` file.
291
+ 6. Anything else produces an error naming the `--lang` override.
272
292
 
273
293
  `--lang` forces the language; `--adapter` picks a non-default adapter within
274
- it (`tdb --lang cpp --adapter lldb-dap ./myprog`).
294
+ it (`tdb --lang cpp --adapter lldb-dap ./myprog`). Rust always requires the
295
+ explicit language selection (`tdb --lang rust target/debug/app`).
275
296
 
276
297
  > **Migration note:** extensionless Python scripts without a `python` shebang
277
298
  > were previously assumed to be Python; they now require `--lang python`.
@@ -304,13 +325,17 @@ process inspectors and wait graph, the evaluate console's trailing-`?` help,
304
325
  `--python`/`--pv`, `--no-subprocess`, automatic child-process attachment, and
305
326
  the post-mortem / `tdb.breakpoint()` hooks (those hooks live inside Python
306
327
  programs by nature). Remote attach (`-r`) also works for Perl (see
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).
328
+ [Perl](#perl), `Devel::TdbRemote` in place of `debugpy.listen()`), Ruby
329
+ (see [Ruby](#ruby), `rdbg --open` in place of `debugpy.listen()`), and for
330
+ Rust and C/C++ native binaries (against a gdbserver/lldb-server stub, with
331
+ a local symbol-bearing executable; see [Rust](#rust) — the same flags work
332
+ for a C/C++ target without `--lang`), but not for Bash, Tcsh, or OCaml. `--terminal` works for every launch-mode
333
+ language — Python, Perl, Bash, Tcsh, Ruby, and C/C++, OCaml native, or Rust
334
+ sessions via `--adapter lldb-dap` — see
335
+ [External Terminal Support](#external-terminal-support).
311
336
 
312
337
  **Bash limitations (v1):** the bash adapter uses bash's own `DEBUG` trap and
313
- has a smaller feature envelope than Python/Perl:
338
+ has a smaller feature set than Python:
314
339
 
315
340
  - Debuggee code that installs its own `DEBUG` trap clobbers the harness;
316
341
  debugging silently degrades to free-running.
@@ -354,9 +379,30 @@ paths and line numbers are preserved in everything tdb displays):
354
379
 
355
380
  See [Tcsh](#tcsh) below for launch details.
356
381
 
382
+ **OCaml limitations (v1):** OCaml has two independent adapters with
383
+ different, non-overlapping gaps; see [OCaml](#ocaml) below for the full
384
+ picture:
385
+
386
+ - **Native (`lldb-dap`/`gdb`):** the Variables view shows no named OCaml
387
+ locals in an OCaml frame (only a `Registers` scope). This is an
388
+ upstream OCaml/DWARF limitation on stock OCaml 5.4, not a `tdb` bug; use
389
+ the bytecode adapter when you need to inspect locals. The evaluate
390
+ console evaluates lldb/C-level expressions (runtime spelunking), not
391
+ OCaml expressions.
392
+ - **Bytecode (`ocamlearlybird`):** single-domain only (no multicore); no
393
+ `pause` while running (so `--run` is unavailable); the evaluate console
394
+ never gets a response for any expression (an `ocamlearlybird` 1.3.6
395
+ limitation); and the fatal-error modal doesn't appear for an uncaught
396
+ exception (`ocamlearlybird` swallows the real exception text before it
397
+ reaches `tdb`). Run the program outside the debugger to see the
398
+ backtrace instead.
399
+ - No Windows support, no remote attach, and a stripped native binary that
400
+ byte-sniffing can't identify needs `--lang ocaml`.
401
+
357
402
  ### C/C++ tips
358
403
 
359
- - Compile with `-g` (ideally `-g -O0`). If no breakpoint in a file can be
404
+ - Compile with `-g` (ideally `-g -O0` or `-g3 -O0`).
405
+ If no breakpoint in a file can be
360
406
  bound, `tdb` prints a console warning suggesting the program may lack
361
407
  debug info.
362
408
  - Stack frames pointing into system libraries often have no source on disk;
@@ -373,6 +419,72 @@ See [Tcsh](#tcsh) below for launch details.
373
419
  requires `--adapter lldb-dap`; GDB's DAP mode has no terminal integration
374
420
  and `tdb` refuses `--terminal` with the default `gdb` adapter.
375
421
 
422
+ ### Rust
423
+
424
+ Rust debugging is intentionally explicit: build a normal debug executable,
425
+ leave it unmodified, and pass that executable with `--lang rust`. Linux and
426
+ macOS are supported; Windows is not yet (MSVC-toolchain binaries carry PDB
427
+ debug info that neither `gdb -i dap` nor this release's probes read). `tdb` does
428
+ not compile, instrument, or auto-detect Rust programs. Core debugging
429
+ (breakpoints, stepping, stacks, variables, remote attach) works with any
430
+ rustc that emits debug info; the concurrency inspector's layout-specific
431
+ *ownership evidence* additionally requires the current stable Rust
432
+ **1.98** standard-library layout — on other versions the workspace still
433
+ opens with stack-based wait classification and a compatibility warning.
434
+ Build with debug information and no optimization for the most useful
435
+ source locations:
436
+
437
+ ```bash
438
+ cargo rustc -- -C debuginfo=2 -C opt-level=0
439
+ # Equivalent direct rustc settings: rustc -C debuginfo=2 -C opt-level=0 src/main.rs
440
+ ```
441
+
442
+ A panic that terminates the debuggee opens the error modal with the parsed
443
+ panic message and backtrace; `tdb` injects `RUST_BACKTRACE=1` into launched
444
+ programs (your own value wins if you set one) so the frames are present.
445
+
446
+ The common commands are:
447
+
448
+ ```bash
449
+ cargo build
450
+ tdb --lang rust target/debug/app
451
+ tdb --lang rust --adapter lldb-dap --run target/debug/app
452
+ tdb --lang rust --adapter lldb-dap --terminal xterm target/debug/app
453
+ tdb --lang rust --adapter gdb --remote-attach host:2345 target/debug/app
454
+ ```
455
+
456
+ `--run` and normal stopped-on-entry launch work with both adapters. On Linux,
457
+ use GDB (the default) or `lldb-dap`; on macOS, use `lldb-dap` (the default).
458
+ External terminals are LLDB-only: GDB's DAP mode cannot provide terminal
459
+ integration. The mode/platform matrix is:
460
+
461
+ | Platform | Normal / `--run` | `--terminal` | `--remote-attach` |
462
+ |----------|------------------|--------------|-------------------|
463
+ | Linux | GDB or `lldb-dap` | `lldb-dap` only | GDB or `lldb-dap` |
464
+ | macOS | `lldb-dap` | `lldb-dap` only | `lldb-dap` |
465
+
466
+ Remote attach connects to a GDB-remote server already running on the target.
467
+ Pass the matching **local, unmodified executable with debug symbols** as the
468
+ program argument; `tdb` requires it for symbols and Rust concurrency evidence.
469
+ When source paths differ, pair `--local-root` with `--remote-root`. Do not
470
+ expose the remote debug port to an untrusted network: prefer an SSH tunnel,
471
+ for example `ssh -L 2345:127.0.0.1:2345 host`, then attach to `127.0.0.1:2345`.
472
+
473
+ In a Rust session the Threads action (`Alt+T`, or the Threads menu entry)
474
+ opens the Rust Concurrency workspace while the debuggee is stopped — a
475
+ fresh, bounded snapshot of threads, wait edges, and findings. The graph is
476
+ best effort: `confirmed` evidence is directly observed by the debugger,
477
+ `probable` is a supported inference, and `unknown` means ownership could not
478
+ be established. A deadlock is confirmed only when every edge in its cycle is
479
+ confirmed. A suspected cycle or whole-program stall is a diagnostic lead, not
480
+ a proof: it means a cycle has incomplete evidence, or all observed application
481
+ threads are blocked while the graph cannot safely invent an owner. Refresh
482
+ after the next stop to collect a new snapshot.
483
+
484
+ The built-in evidence recognizes current `std` synchronization layouts. A
485
+ future helper crate may expose stable, application-provided synchronization
486
+ metadata for broader primitives and stronger ownership evidence.
487
+
376
488
  ### Perl
377
489
 
378
490
  `tdb` bundles its own Perl adapter (`perl-tdb`) so only need
@@ -549,6 +661,119 @@ text directly in the paused shell. It can inspect *and mutate* state
549
661
  (`set name = value` takes effect immediately), and a syntax error in
550
662
  evaluated text can terminate the debuggee; there is no isolation.
551
663
 
664
+ ### Ruby
665
+
666
+ tdb debugs Ruby via the [debug gem](https://github.com/ruby/debug)'s
667
+ `rdbg` (Ruby >= 3.1 ships it; otherwise `gem install debug`; tdb needs
668
+ debug >= 1.9). `rdbg` must be on PATH, or point tdb at it with
669
+ `{"adapters": {"rdbg": "/path/to/rdbg"}}` in config.json.
670
+
671
+ ```bash
672
+ tdb script.rb # launch, stop at first line
673
+ tdb --run script.rb # run immediately, debug on demand
674
+ tdb --terminal script.rb # program I/O in its own terminal
675
+ ```
676
+
677
+ Remote attach: start the program with
678
+ `rdbg --open --port 5678 --host 0.0.0.0 script.rb` (add `--nonstop` to
679
+ let it run before you attach), then `tdb -r HOST:5678 --lang ruby`.
680
+ Note: rdbg's `--cookie` authentication is not part of DAP and is not
681
+ supported; bind to localhost and tunnel over SSH instead.
682
+ `--local-root`/`--remote-root` path mappings are not supported for Ruby
683
+ yet. Bundler projects work when your environment resolves `rdbg`
684
+ (`gem install debug` into the project's Ruby); there is no `bundle
685
+ exec` integration yet.
686
+
687
+ > **Startup note:** the debug gem's `rdbg` has an occasional handshake
688
+ > race on launch; tdb detects a stalled/corrupted handshake and retries
689
+ > once automatically with a fresh `rdbg`, so this is usually invisible.
690
+
691
+ ### OCaml
692
+
693
+ `tdb` debugs OCaml through two independent adapters, chosen automatically
694
+ from how the executable was built:
695
+
696
+ - **Native** (`ocamlopt`/dune's default build) → **`lldb-dap`** (default;
697
+ `--adapter gdb` also works). OCaml 5's domains show up as threads.
698
+ - **Bytecode** (`ocamlc -g`) → **`ocamlearlybird`** (`opam install
699
+ earlybird`). Single-domain only, but with rich, real OCaml locals.
700
+
701
+ **Build requirement:** compile with `-g` (dune's dev profile already
702
+ does). Always pass the **built executable** to `tdb`, never the `.ml`
703
+ source, as in `tdb ./_build/default/bin/main.exe`, not `tdb main.ml` (which
704
+ errors with this exact guidance).
705
+
706
+ **OCaml version:** tdb requires **OCaml ≥ 4.12**. Older runtimes print
707
+ `Printexc` backtraces without function names, which the fatal-error
708
+ modal's parser does not understand (frames would be dropped and the
709
+ modal would wrongly suggest recompiling with `-g`).
710
+
711
+ ```bash
712
+ tdb ./_build/default/bin/main.exe # native, lldb-dap
713
+ tdb --adapter gdb ./_build/default/bin/main.exe # native, gdb
714
+ tdb ./_build/default/bin/main.byte # bytecode, ocamlearlybird
715
+ ```
716
+
717
+ A stripped native binary that byte-sniffing can't identify as OCaml falls
718
+ back to C/C++; force it with `--lang ocaml`. Override the adapter or point
719
+ at a non-`PATH` install in `config.json` (see
720
+ [Configuration](#configuration)):
721
+
722
+ ```json
723
+ {
724
+ "adapters": {"ocamlearlybird": "/path/to/ocamlearlybird"},
725
+ "default_adapters": {"ocaml": "gdb"}
726
+ }
727
+ ```
728
+
729
+ **Domains as threads (native only):** OCaml 5's domains are presented in
730
+ the Threads modal (`Alt+T` or the **Threads (N)** menu label) as `Domain 0
731
+ (main)`, `Domain 1`, and so on, in creation order. The runtime's internal
732
+ "backup thread" (one per domain, used for I/O blocking) is hidden by
733
+ default since it's never running user code; press `a` in the Threads
734
+ modal to reveal it alongside the domains. Breakpoints, stepping, continue,
735
+ and pause all work normally against any domain. In both the Stack view
736
+ and the Threads modal, OCaml frame names are demangled from the
737
+ runtime's raw `camlModule__name_NNN` convention into readable
738
+ `Module.name` form (e.g. `camlOcaml_domains.worker_297` displays as
739
+ `Ocaml_domains.worker`); runtime C frames (`caml_start_program`,
740
+ `caml_callback_exn`, and the like) are shown as-is, unchanged.
741
+
742
+ **Variables view (native):** stock OCaml 5.4 native DWARF shows **no
743
+ named locals**. The `scopes`/`variables` round trip succeeds, but every
744
+ OCaml frame's `Locals` and `Globals` scopes come back empty; only a
745
+ `Registers` scope has data (the raw register file, not decoded OCaml
746
+ values). This is an upstream OCaml compiler/DWARF limitation on the
747
+ verified toolchain (OCaml 5.4.0 / lldb 21), not a `tdb` bug. If you need
748
+ to inspect local variables, use the bytecode adapter instead.
749
+
750
+ **Variables view (bytecode):** `ocamlearlybird` reports real local names
751
+ and values, exactly like Python locals.
752
+
753
+ **Evaluate console:** the two adapters give fundamentally different
754
+ consoles. Native (`lldb-dap`) evaluates **lldb/C-level expressions**.
755
+ This is runtime spelunking (registers, raw memory, C symbol names), not
756
+ OCaml expression evaluation. Bytecode (`ocamlearlybird`) is meant to
757
+ evaluate real OCaml expressions in scope, but in the verified
758
+ `ocamlearlybird` 1.3.6 release its `evaluate` request never responds (a
759
+ confirmed upstream limitation, not specific to `tdb`); the console
760
+ silently returns nothing for any expression.
761
+
762
+ **Uncaught exceptions:** native sessions set a breakpoint on
763
+ `caml_fatal_uncaught_exception` so the debugger stops there, and `tdb`
764
+ also parses the `OCAMLRUNPARAM=b` backtrace it injects into the debuggee's
765
+ environment once the process exits, opening the same error modal Python
766
+ tracebacks get. Bytecode sessions do **not** get this: `ocamlearlybird`
767
+ intercepts the uncaught exception itself and only reports a generic
768
+ "Program exited due to Uncaught_exc" message, so the real exception text
769
+ never reaches `tdb`. Run the program outside the debugger
770
+ (`OCAMLRUNPARAM=b ./main.byte`) to see its backtrace.
771
+
772
+ **Limitations (v1):** no Windows support; no remote attach; bytecode
773
+ sessions can't `pause` a running program (so `--run` is unavailable for
774
+ bytecode. Native sessions support `--run` normally); `Unix.fork` and Eio
775
+ fibers are out of scope (only OCaml 5 domains are presented as threads).
776
+
552
777
  ## Layout
553
778
 
554
779
  ```
@@ -604,6 +829,11 @@ A cursor line (blue) tracks your position; the current execution line is highlig
604
829
  | `Alt+A` | Async Tasks |
605
830
  | `Alt+H` | Help (Documentation, About) |
606
831
 
832
+ File > Open works for every supported language: the file picker filters to
833
+ the current session's language (by extension) and refuses a pick that
834
+ detects as a different language, so you can only switch to another script
835
+ written in the same language as the one currently being debugged.
836
+
607
837
  **Navigation (vim-style by default):**
608
838
 
609
839
  By default the Code View is in Debug mode. Hit `Escape` to switch to Navigate mode
@@ -661,7 +891,7 @@ results = await asyncio.gather(
661
891
  fetch(2, 1),
662
892
  fetch(3, 3),
663
893
  )
664
- print(results) # next stop lands here, not on each sub-line above
894
+ print(results) # next stop lands here, not on each sub-line above
665
895
  ```
666
896
 
667
897
  lands on `print(results)`, not on each interior sub-line of the `gather` call. Switch to
@@ -771,6 +1001,7 @@ need to launch through `tdb` up front. Install the hook once at the top of your
771
1001
  ```python
772
1002
  import sys
773
1003
  import tdb
1004
+
774
1005
  sys.excepthook = tdb.exception_hook
775
1006
  ```
776
1007
 
@@ -819,6 +1050,7 @@ continuing--use `tdb.breakpoint()`:
819
1050
  ```python
820
1051
  import tdb
821
1052
 
1053
+
822
1054
  def compute(n):
823
1055
  total = sum(range(n))
824
1056
  tdb.breakpoint() # pause here and drop into tdb
@@ -961,6 +1193,7 @@ where you want to attach the debugger:
961
1193
  ```python
962
1194
  # In the target program:
963
1195
  import debugpy
1196
+
964
1197
  debugpy.listen(("0.0.0.0", 5678))
965
1198
  print("Waiting for tdb to attach on port 5678...")
966
1199
  debugpy.wait_for_client() # optional: pause until debugger connects
@@ -1028,12 +1261,14 @@ tdb --terminal xterm my_tui_app.py
1028
1261
  ```
1029
1262
 
1030
1263
  `--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.
1264
+ attach to): Python, Perl, Bash, Tcsh, Ruby, and C/C++ or native OCaml
1265
+ sessions via `--adapter lldb-dap`. Neither `gdb -i dap` (the default C/C++
1266
+ adapter, and an alternate for native OCaml) nor `ocamlearlybird` (the
1267
+ bytecode OCaml adapter) has any terminal integration -- `tdb` rejects
1268
+ `--terminal` up front for both, with an error pointing at `--adapter
1269
+ lldb-dap` instead. `--terminal` also only applies when `tdb` launches the
1270
+ program itself; it is rejected for remote-attach (`-r`), since there's no
1271
+ program for `tdb` to spawn a terminal around.
1037
1272
 
1038
1273
  The debuggee runs in a separate window of the specified terminal, including
1039
1274
  all of its stdin/stdout/stderr -- keyboard input, program output, and
@@ -1048,9 +1283,17 @@ This feature only works in graphical environments where external terminals are a
1048
1283
 
1049
1284
  ### Run Mode (`--run`)
1050
1285
 
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.
1286
+ `tdb`'s run mode invokes an unmodified program nearly at full speed, without bringing up the TUI.
1287
+ At any point afterwards, hitting `Ctrl-C` in that terminal pauses the program
1288
+ and drops you into the full TUI.
1289
+ From there you can inspect the program's state, navigate the stack, set and continue
1290
+ to breakpoints, and so on. After you finish inspecting, hitting `q` to quit brings up
1291
+ a modal that lets you either detach from the TUI and let the program resume running,
1292
+ or lets you terminate it.
1293
+
1294
+ The primary use cases for run mode are: (1) allowing inspection of a program that
1295
+ appears hung, and (2) finding where a program execution path lands after it
1296
+ receives a problematic input.
1054
1297
 
1055
1298
  ```bash
1056
1299
  tdb --run my_program.py args...
@@ -1084,8 +1327,10 @@ nonzero `sys.exit()`, which `debugpy` treats as an uncaught `SystemExit` -- `tdb
1084
1327
  the TUI at the point of failure instead of exiting silently, so you can inspect the
1085
1328
  crash. Exit-code passthrough applies only to a clean exit during the headless phase.
1086
1329
 
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.
1330
+ Supported languages: Python, Perl, Bash, Tcsh, Ruby, C/C++ (both `gdb` and
1331
+ `lldb-dap`), and native OCaml (both `gdb` and `lldb-dap`; bytecode/
1332
+ `ocamlearlybird` sessions don't support pause, so `--run` is unavailable
1333
+ for those). `--run` is rejected up front for any other language.
1089
1334
 
1090
1335
  **Limitation:** pausing is cooperative. If the debuggee is blocked inside a single
1091
1336
  blocking external call or syscall, the pause can't land until that call returns --
@@ -1129,7 +1374,9 @@ Both the interactive TUI and the JSON-RPC server run simultaneously.
1129
1374
  ### RPC Protocol
1130
1375
 
1131
1376
  Send POST requests with `{"action": "...", "params": [...]}`. Responses return
1132
- `{"timestamp": "...", "success": true/false, "value": "..."}`.
1377
+ `{"timestamp": "...", "success": true/false, "value": "..."}`. Actions with a
1378
+ structured result (today only `rust_concurrency`) additionally carry a `data`
1379
+ object holding the full JSON payload; `value` then holds a one-line summary.
1133
1380
 
1134
1381
  ```bash
1135
1382
  # Check status
@@ -1187,6 +1434,7 @@ curl -s -X POST http://127.0.0.1:8150/rpc \
1187
1434
  | `list_tasks` | `[]` | List all asyncio tasks |
1188
1435
  | `inspect_task` | `["task_name"]` | Inspect a specific asyncio task |
1189
1436
  | `wait_graph` | `[]` | Show wait graph + any deadlock cycles |
1437
+ | `rust_concurrency` | `[]` | Structured Rust thread/wait/deadlock snapshot in `data` (Rust sessions, stopped) |
1190
1438
  | `restart` | `[]` | Restart session (preserves breakpoints) |
1191
1439
  | `quit` | `[]` | Shut down |
1192
1440
 
@@ -1269,7 +1517,7 @@ point), `tdb --mcp` (the main CLI with the `--mcp` switch), and
1269
1517
  `python -m tdb.mcp` (module form). Pick whichever matches how your MCP
1270
1518
  client expects to launch servers.
1271
1519
 
1272
- ### Tool surface (16 tools, curated)
1520
+ ### Tool surface (17 tools, curated)
1273
1521
 
1274
1522
  | Cluster | Tools |
1275
1523
  |---------|-------|
@@ -1277,7 +1525,7 @@ client expects to launch servers.
1277
1525
  | Control | `control(action, timeout_s=30)` where `action ∈ {continue, next, step_in, step_out, pause, wait_for_stop}` |
1278
1526
  | Inspection | `inspect(expressions)`, `read_source(file_path)`, `stack_trace()`, `status()`, `get_output()` |
1279
1527
  | Breakpoints | `set_breakpoint(spec, condition?, hit_condition?)`, `remove_breakpoint(spec)`, `list_breakpoints()` |
1280
- | Differentiators | `threads(thread_id?)`, `tasks(task_name?)`, `processes(name_or_pid?)`, `wait_graph()` |
1528
+ | Differentiators | `threads(thread_id?)`, `tasks(task_name?)`, `processes(name_or_pid?)`, `wait_graph()`, `rust_concurrency()` |
1281
1529
 
1282
1530
  `control` is intentionally one tool that takes an action enum. The six
1283
1531
  underlying RPC actions share a return shape, and agents perform
@@ -1288,9 +1536,10 @@ single optional argument for the same reason.
1288
1536
  `debug_launch` accepts optional `lang` and `adapter` parameters mirroring the
1289
1537
  CLI's `--lang`/`--adapter`; when omitted, the language is auto-detected from
1290
1538
  `program`, so an agent can hand it a compiled binary directly. The
1291
- `tasks`/`processes`/`wait_graph` tools stay registered for every language but
1539
+ exception is Rust: compiled native binaries require explicit `lang="rust"`.
1540
+ The `tasks`/`processes`/`wait_graph` tools stay registered for every language but
1292
1541
  return a structured "not supported when debugging C/C++"-style error for
1293
- non-Python debuggees.
1542
+ non-Python debuggees; `rust_concurrency` does the same for non-Rust debuggees.
1294
1543
 
1295
1544
  ### Agent flow for a long-running step
1296
1545
 
@@ -1408,6 +1657,8 @@ fires as expected.
1408
1657
  - [textual](https://github.com/Textualize/textual) : TUI framework
1409
1658
  - [debugpy](https://github.com/microsoft/debugpy) : Debug Adapter Protocol implementation for Python
1410
1659
  - [gdb](https://sourceware.org/gdb/) / [lldb-dap](https://lldb.llvm.org/resources/lldbdap.html) : optional, user-installed DAP adapters for C/C++
1660
+ - [debug gem](https://github.com/ruby/debug) (`rdbg`) : optional, user-installed DAP adapter for Ruby
1661
+ - [ocamlearlybird](https://github.com/hackwaly/ocamlearlybird) : optional, user-installed DAP adapter for OCaml bytecode (native OCaml reuses `gdb`/`lldb-dap`)
1411
1662
  - [pygments](https://pygments.org/) : Syntax highlighting
1412
1663
  - [FastAPI](https://fastapi.tiangolo.com/) + [uvicorn](https://www.uvicorn.org/) : JSON-RPC server
1413
1664