textual-debugger 0.2.10__tar.gz → 0.2.12__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.
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/PKG-INFO +128 -41
- {textual_debugger-0.2.10/src/tdb → textual_debugger-0.2.12}/README.md +126 -39
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/pyproject.toml +2 -2
- {textual_debugger-0.2.10 → textual_debugger-0.2.12/src/tdb}/README.md +126 -39
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/__init__.py +1 -1
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/_timeouts.py +7 -0
- textual_debugger-0.2.12/src/tdb/adapters/perl/padwalker.py +286 -0
- textual_debugger-0.2.12/src/tdb/adapters/perl/padwalker_src/Makefile.PL +38 -0
- textual_debugger-0.2.12/src/tdb/adapters/perl/padwalker_src/PadWalker.pm +160 -0
- textual_debugger-0.2.12/src/tdb/adapters/perl/padwalker_src/PadWalker.xs +645 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/perl/session.py +15 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/app.py +28 -16
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/app_handlers/dap_events.py +3 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/app_helpers.py +51 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/cli.py +183 -11
- textual_debugger-0.2.12/src/tdb/examine.py +302 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/mcp/server.py +12 -9
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/mcp/session.py +1 -1
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/replay.py +28 -8
- textual_debugger-0.2.12/src/tdb/replay_tui.py +311 -0
- textual_debugger-0.2.12/src/tdb/run_mode.py +514 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/server/handlers.py +3 -3
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/session/controller.py +90 -16
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/evaluate_console.py +12 -7
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/textual_debugger.egg-info/PKG-INFO +128 -41
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/textual_debugger.egg-info/SOURCES.txt +6 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/textual_debugger.egg-info/requires.txt +1 -1
- textual_debugger-0.2.10/src/tdb/run_mode.py +0 -305
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/LICENSE +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/setup.cfg +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/__main__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/bash/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/bash/__main__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/bash/declares.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/bash/server.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/bash/session.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/bash/tdb_harness.sh +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/ocaml/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/ocaml/lldb_formatters.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/perl/Devel/TdbCompile.pm +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/perl/Devel/TdbRemote.pm +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/perl/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/perl/__main__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/perl/helpers.pl +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/perl/protocol.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/perl/refs.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/perl/server.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/powershell/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/powershell/__main__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/powershell/locate.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/powershell/output.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/powershell/server.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/powershell/tdb_launch.ps1 +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/ruby/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/ruby/__main__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/ruby/server.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/seqs.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/tcsh/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/tcsh/__main__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/tcsh/breakpoints.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/tcsh/cli.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/tcsh/guardian.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/tcsh/inspect.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/tcsh/instrumenter.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/tcsh/models.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/tcsh/protocol.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/tcsh/runtime.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/tcsh/scanner.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/tcsh/server.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/tcsh/session.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/adapters/tcsh/transport.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/app_handlers/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/app_handlers/inspection.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/app_handlers/routing.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/app_handlers/ui_panels.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/breakpoint_hook.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/dap/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/dap/client.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/dap/messages.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/dap/protocol.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/dap/reverse.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/dap/types.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/eval_mode.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/go_concurrency/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/go_concurrency/analyzer.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/go_concurrency/classifier.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/go_concurrency/collector.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/go_concurrency/models.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/inspection.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/inspection_full.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/keybindings/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/languages/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/languages/base.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/languages/bash.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/languages/cpp.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/languages/errors.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/languages/go.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/languages/ocaml.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/languages/perl.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/languages/powershell.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/languages/python.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/languages/registry.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/languages/ruby.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/languages/rust.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/languages/tcsh.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/mcp/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/mcp/__main__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/persist.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/post_mortem.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/processes_cache.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/rust_concurrency/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/rust_concurrency/analyzer.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/rust_concurrency/classifier.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/rust_concurrency/collector.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/rust_concurrency/models.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/rust_concurrency/probes/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/rust_concurrency/probes/base.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/rust_concurrency/probes/gdb.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/rust_concurrency/probes/gdb_script.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/rust_concurrency/probes/lldb.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/rust_concurrency/probes/lldb_script.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/server/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/server/app.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/server/event_handler.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/server/rpc_types.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/server/runner.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/session/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/session/child_processes.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/session/errors.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/session/event_bus.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/session/inspect_service.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/session/messages.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/session/post_mortem_loader.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/session/recorder.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/session/state.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/session/statement_stepper.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/session/terminal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/session/textual_handler.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/source_analysis.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/__init__.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/_inspection_modal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/async_tasks_modal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/breakpoint_view.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/code_view.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/console_view.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/full_contents_modal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/goroutines_modal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/menu_bar.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/modals.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/processes_modal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/rust_concurrency_modal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/stack_view.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/status_bar.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/threads_modal.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/tdb/widgets/variable_view.py +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/textual_debugger.egg-info/dependency_links.txt +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/src/textual_debugger.egg-info/entry_points.txt +0 -0
- {textual_debugger-0.2.10 → textual_debugger-0.2.12}/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.
|
|
3
|
+
Version: 0.2.12
|
|
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:
|
|
@@ -53,7 +53,7 @@ Requires-Dist: debugpy>=1.8.12
|
|
|
53
53
|
Requires-Dist: pygments>=2.17.0
|
|
54
54
|
Requires-Dist: fastapi>=0.115.0
|
|
55
55
|
Requires-Dist: uvicorn>=0.30.0
|
|
56
|
-
Requires-Dist: mcp<
|
|
56
|
+
Requires-Dist: mcp<3.0.0,>=2.0.0
|
|
57
57
|
Provides-Extra: dev
|
|
58
58
|
Requires-Dist: pytest; extra == "dev"
|
|
59
59
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -115,7 +115,7 @@ It specifically supports modules
|
|
|
115
115
|
|
|
116
116
|
- supports remote attachment to Python, Perl, Ruby, Rust, C/C++, and Go programs
|
|
117
117
|
|
|
118
|
-
- includes a JSON-RPC server mode, an MCP mode, and a `SKILL.md` file that enable
|
|
118
|
+
- includes a JSON-RPC server mode, an MCP (v2) mode, and a `SKILL.md` file that enable
|
|
119
119
|
programmatic debug control, making it suitable for
|
|
120
120
|
automated, headless debugging workflows and AI-assisted debugging
|
|
121
121
|
|
|
@@ -129,28 +129,6 @@ to have `tdb` pop open automatically at the first uncaught exception
|
|
|
129
129
|
making it suitable for operation in non-graphical environments (mouse support is
|
|
130
130
|
available in graphical environments)
|
|
131
131
|
|
|
132
|
-
## Acknowledgments
|
|
133
|
-
|
|
134
|
-
Thank you:
|
|
135
|
-
|
|
136
|
-
- Will McGugan for the amazing `textual` module.
|
|
137
|
-
`tdb` would be a pale shadow of itself had I used any other TUI framework.
|
|
138
|
-
Fantastic work, Will.
|
|
139
|
-
|
|
140
|
-
- Microsoft for the Debug Adapter Protocol (DAP) and releasing
|
|
141
|
-
its implementation in `debugpy` and the Python Debugger extension for Visual Studio Code
|
|
142
|
-
as open source.
|
|
143
|
-
|
|
144
|
-
- Anthropic, for providing access to Claude Code through the
|
|
145
|
-
[Claude for Open Source](https://claude.com/contact-sales/claude-for-oss) program.
|
|
146
|
-
`tdb` was made almost entirely with Claude Code.
|
|
147
|
-
|
|
148
|
-
- OpenAI, for providing access to Codex through the
|
|
149
|
-
[Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
|
|
150
|
-
|
|
151
|
-
This project was inspired by Andreas Klöckner's excellent [pudb](https://pypi.org/project/pudb/)
|
|
152
|
-
Python debugger.
|
|
153
|
-
|
|
154
132
|
## Gallery
|
|
155
133
|
<p align="center">
|
|
156
134
|
<img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_breakpoint.png" alt="at breakpoint" width="300">
|
|
@@ -190,6 +168,10 @@ uvx --from textual-debugger tdb my_program.py
|
|
|
190
168
|
# show comprehensive documentation in a terminal-based Markdown viewer
|
|
191
169
|
tdb --doc
|
|
192
170
|
|
|
171
|
+
# show where tdb is installed, where its bundled Perl PadWalker module
|
|
172
|
+
# lives, and the Help > About text
|
|
173
|
+
tdb --info
|
|
174
|
+
|
|
193
175
|
# debug a script (stops at first line by default)
|
|
194
176
|
tdb my_program.py
|
|
195
177
|
|
|
@@ -337,7 +319,7 @@ non-standard location, or change a language's default adapter, add to
|
|
|
337
319
|
Core debugging works identically for every language: breakpoints (incl.
|
|
338
320
|
conditions and persistence), stepping, continue/pause, run-to-cursor, stack
|
|
339
321
|
navigation, variable inspection, the evaluate console, syntax highlighting,
|
|
340
|
-
and the JSON-RPC / MCP programmatic modes.
|
|
322
|
+
and the JSON-RPC / MCP (v2) programmatic modes.
|
|
341
323
|
|
|
342
324
|
Python-specific features are hidden or return "not supported for this
|
|
343
325
|
language" message when debugging other languages: statement-granularity
|
|
@@ -541,6 +523,24 @@ metadata for broader primitives and stronger ownership evidence.
|
|
|
541
523
|
`perl` ≥ 5.18 on `PATH`. It drives stock `perl5db` under the
|
|
542
524
|
hood, so it works with any Perl already on the system.
|
|
543
525
|
|
|
526
|
+
The [PadWalker](https://metacpan.org/pod/PadWalker) module v2.5, by Robin Houston,
|
|
527
|
+
is bundled with `tdb`. PadWalker provides a more complete view of lexical variables
|
|
528
|
+
in outer/caller frames and therefore provides a richer debugging experience.
|
|
529
|
+
PadWalker is an XS (compiled) module, so the bundled copy has to be built
|
|
530
|
+
against the exact perl that runs your program. `tdb` does this for you on the
|
|
531
|
+
first Perl launch: if the perl in use cannot already load PadWalker (from CPAN
|
|
532
|
+
or a distro package), `tdb` compiles the bundled sources, caches the result
|
|
533
|
+
per interpreter under its config directory (`~/.config/tdb/padwalker/` on
|
|
534
|
+
Linux/macOS, `%APPDATA%\\tdb\\padwalker\\` on Windows), and prepends that
|
|
535
|
+
directory to the debuggee's `PERL5LIB`. The build takes about a second and
|
|
536
|
+
needs perl's headers (`libperl-dev` on Debian/Ubuntu, `perl-devel` on
|
|
537
|
+
Fedora/RHEL) plus a C compiler; Strawberry Perl on Windows ships both. When
|
|
538
|
+
the build is not possible, `tdb` prints a one-line notice on the console and
|
|
539
|
+
falls back to a read-only pad walk, so outer-frame lexicals degrade but
|
|
540
|
+
debugging otherwise proceeds. `tdb --info` reports the source directory, the
|
|
541
|
+
cache directory, and whether a build exists for the `perl` on your `PATH`.
|
|
542
|
+
`TDB_PADWALKER_CACHE` overrides the cache location.
|
|
543
|
+
|
|
544
544
|
**Launching a script:**
|
|
545
545
|
|
|
546
546
|
```bash
|
|
@@ -637,13 +637,6 @@ export PERL5LIB=/opt/tdb-perl:$PERL5LIB
|
|
|
637
637
|
the two files in the same relative layout shown above; `helpers.pl` is a
|
|
638
638
|
sibling of the `Devel/` directory, not inside it.)
|
|
639
639
|
|
|
640
|
-
**PadWalker (optional but recommended):** inspecting lexical (`my`)
|
|
641
|
-
variables in the *current* frame always works. Lexicals in outer/caller
|
|
642
|
-
frames need the `PadWalker` module installed on the debuggee's Perl; without
|
|
643
|
-
it, tdb falls back to a read-only pad walk that can't reach fully into
|
|
644
|
-
enclosing scopes, and outer-frame variable listings degrade accordingly.
|
|
645
|
-
Install with `cpanm PadWalker` (or your distro's package) for full fidelity.
|
|
646
|
-
|
|
647
640
|
**Pause is unavailable in attach mode.** Launch-mode sessions (`tdb
|
|
648
641
|
script.pl`) support pausing a running program at any time. Remote-attach
|
|
649
642
|
sessions don't. Asynchronous pause (as Python gets via `debugpy`) needs a control channel
|
|
@@ -1517,6 +1510,54 @@ available (breakpoints, stepping, variable inspection, the evaluate console). Th
|
|
|
1517
1510
|
debuggee itself never receives the signal (`tdb`'s adapter runs in its own process
|
|
1518
1511
|
group), so its own `SIGINT` handling is undisturbed.
|
|
1519
1512
|
|
|
1513
|
+
**Examining without opening the TUI:** press `Ctrl-\` in the terminal (`Ctrl-Break` on
|
|
1514
|
+
Windows), or, on Unix, send `SIGUSR2` to `tdb`'s pid: `kill -USR2 <tdb pid>`.
|
|
1515
|
+
|
|
1516
|
+
**Windows caveat:** the Ctrl-Break trigger is untested. Unlike Ctrl-C, a console
|
|
1517
|
+
Ctrl-Break is delivered to every process attached to the console, including the debug
|
|
1518
|
+
adapter and the program itself, so it may terminate the program instead of snapshotting
|
|
1519
|
+
it. There is no signal-based alternative on Windows.
|
|
1520
|
+
|
|
1521
|
+
`tdb` pauses the program, writes one JSON line describing the call stack of every thread
|
|
1522
|
+
(plus asyncio tasks and multiprocessing children for Python, goroutines for Go, and
|
|
1523
|
+
the concurrency snapshot for Rust), and resumes the program. Ctrl-C behavior is
|
|
1524
|
+
unchanged. By default the line goes to stdout; `--examine-log DEST` sends it to a file
|
|
1525
|
+
instead (`-` means stdout; repeat the flag to write to several places):
|
|
1526
|
+
|
|
1527
|
+
```bash
|
|
1528
|
+
tdb --run --examine-log hang.jsonl my_program.py # file only, appended
|
|
1529
|
+
tdb --run --examine-log - --examine-log hang.jsonl my_program.py # both
|
|
1530
|
+
```
|
|
1531
|
+
|
|
1532
|
+
Each record looks like (abridged):
|
|
1533
|
+
|
|
1534
|
+
```json
|
|
1535
|
+
{"schema":1,"seq":1,"trigger":"SIGQUIT","status":"ok",
|
|
1536
|
+
"requested_at":"2026-09-07T14:02:11.482-07:00","landed_at":"2026-09-07T14:02:11.511-07:00",
|
|
1537
|
+
"elapsed_s":87.3,"language":"python","program":"/home/al/work/app.py",
|
|
1538
|
+
"processes":[{"pid":41213,"role":"parent","name":null,
|
|
1539
|
+
"threads":[{"id":1,"name":"MainThread","frames":[
|
|
1540
|
+
{"function":"wait","file":"/usr/lib/python3.13/threading.py","line":359},
|
|
1541
|
+
{"function":"main","file":"/home/al/work/app.py","line":42}]}],
|
|
1542
|
+
"tasks":[{"name":"worker-2","state":"PENDING","awaiting":"Lock.acquire","frames":["run (/home/al/work/app.py:18)"]}]}],
|
|
1543
|
+
"errors":[]}
|
|
1544
|
+
```
|
|
1545
|
+
|
|
1546
|
+
Frames are innermost first. `tasks` appears only for Python programs with live asyncio
|
|
1547
|
+
tasks; `goroutines` only for Go; `rust_concurrency` only for Rust. `status` is
|
|
1548
|
+
`"pending"` when the pause could not land within a few seconds (the program is blocked
|
|
1549
|
+
inside a single call) -- a second record with the same `seq` and `status: "ok"` follows
|
|
1550
|
+
when it does land. If you press Ctrl-C or the program exits before it lands, no
|
|
1551
|
+
completing record is written. `errors` lists any sub-collector that failed, so a partial
|
|
1552
|
+
record is explicit about what is missing. A stderr line confirms each capture and where
|
|
1553
|
+
it went.
|
|
1554
|
+
|
|
1555
|
+
Narrow a long capture with `jq`, for example only threads with a frame in your own code:
|
|
1556
|
+
|
|
1557
|
+
```bash
|
|
1558
|
+
jq -c '.processes[].threads[] | select(any(.frames[]; .file != null and (.file|test("/work/"))))' hang.jsonl
|
|
1559
|
+
```
|
|
1560
|
+
|
|
1520
1561
|
**Quitting an adopted session** adds a third choice to the usual quit dialog:
|
|
1521
1562
|
- `d` (or `q`) -- detach and resume: the program keeps running headlessly; interrupt it
|
|
1522
1563
|
again later the same way.
|
|
@@ -1541,7 +1582,7 @@ language.
|
|
|
1541
1582
|
blocking external call or syscall, the pause can't land until that call returns --
|
|
1542
1583
|
`tdb` prints a notice and opens the TUI once the stop actually arrives.
|
|
1543
1584
|
|
|
1544
|
-
**Incompatible flags:** `-r`, `-k`/`-t`, `--record`, `--replay`, `--server`,
|
|
1585
|
+
**Incompatible flags:** `-r`, `-k`/`-t`, `--record`, `--replay`, `--replay-tui`, `--server`,
|
|
1545
1586
|
`--headless`, `--mcp`, `--terminal`.
|
|
1546
1587
|
|
|
1547
1588
|
### Eval Mode (`-e`/`--eval`)
|
|
@@ -1584,7 +1625,7 @@ evaluated in each child process that reaches it.
|
|
|
1584
1625
|
the eval point matches that bound line, so the expression still runs.
|
|
1585
1626
|
|
|
1586
1627
|
**Incompatible flags:** `--run`, `-r`, `-a`, `-k`/`-t`, `--record`,
|
|
1587
|
-
`--replay`, `--server`, `--headless`, `--mcp`, `--terminal`.
|
|
1628
|
+
`--replay`, `--replay-tui`, `--server`, `--headless`, `--mcp`, `--terminal`.
|
|
1588
1629
|
|
|
1589
1630
|
### Keybinding Schemes
|
|
1590
1631
|
|
|
@@ -1706,14 +1747,29 @@ written to `session.jsonl` as
|
|
|
1706
1747
|
JSON-RPC commands. Works with launch mode (any language) and `-r`
|
|
1707
1748
|
remote attach.
|
|
1708
1749
|
|
|
1709
|
-
Replay it
|
|
1710
|
-
|
|
1750
|
+
Replay it three ways:
|
|
1751
|
+
|
|
1752
|
+
- `tdb --replay-tui session.jsonl` opens the normal TUI on the recorded
|
|
1753
|
+
program and performs every recorded action in front of you, at the
|
|
1754
|
+
recorded pace: breakpoints appear in the Code View, steps and continues
|
|
1755
|
+
move the cursor, Evaluate entries echo into the console with their
|
|
1756
|
+
results, stack navigation and variable expansion play out in their
|
|
1757
|
+
panels. The title bar shows progress (`tdb ▶ session.jsonl 3/11`) and a
|
|
1758
|
+
toast announces each action (`--replay-quiet` turns those toasts off).
|
|
1759
|
+
A failed action raises an error toast and replay continues. When the recording does not end with `quit`, the
|
|
1760
|
+
debugger stays open at the final stop for you to carry on by hand.
|
|
1761
|
+
`--replay-interval S` waits a fixed S seconds before each action
|
|
1762
|
+
instead of reproducing the recorded gaps. `--replay-timeout S` bounds
|
|
1763
|
+
each stop-wait (default 30 s). Exit code 0
|
|
1764
|
+
iff every action succeeded. Don't type into the TUI while it is
|
|
1765
|
+
replaying: stray keystrokes interleave with the recorded actions.
|
|
1711
1766
|
- `tdb --replay session.jsonl` launches the recorded
|
|
1712
1767
|
program headless, feeds every recorded command through the same RPC
|
|
1713
1768
|
dispatch `tdb --server` uses, and prints a transcript (recorded time,
|
|
1714
1769
|
command, verbatim result, interleaved program output). Exit code 0 iff
|
|
1715
1770
|
every command succeeded. Add `--timing` to reproduce the original
|
|
1716
|
-
pacing
|
|
1771
|
+
pacing or `--replay-interval S` for a fixed delay before each command,
|
|
1772
|
+
`--replay-timeout S` to bound each stop-wait (default 30 s).
|
|
1717
1773
|
- Against a live server: start `tdb --server prog.py`, then feed line 2
|
|
1718
1774
|
onward of the file to `POST /rpc` . Each line is a valid request body:
|
|
1719
1775
|
|
|
@@ -1730,9 +1786,10 @@ lists), breakpoint enable/disable toggles, variable expansions when the
|
|
|
1730
1786
|
adapter reports no `evaluateName` (currently the Perl adapter), and
|
|
1731
1787
|
File > Open program switches.
|
|
1732
1788
|
|
|
1733
|
-
## MCP Integration
|
|
1789
|
+
## MCP v2 Integration
|
|
1734
1790
|
|
|
1735
|
-
tdb ships a Model Context Protocol (MCP)
|
|
1791
|
+
tdb ships a Model Context Protocol (MCP), version 2,
|
|
1792
|
+
server (`tdb-mcp`) that exposes
|
|
1736
1793
|
the debugger as a curated set of tools an AI agent can call. The MCP
|
|
1737
1794
|
server is a third in-process consumer of the same dispatch handlers the
|
|
1738
1795
|
TUI and the HTTP server use so an agent gets the same lock semantics,
|
|
@@ -1830,7 +1887,7 @@ usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
|
|
|
1830
1887
|
[--keybindings {default,vim,emacs}]
|
|
1831
1888
|
[--terminal {xterm,konsole,gnome-terminal,ghostty,kitty,iterm2,warp,wezterm,terminator}]
|
|
1832
1889
|
[--local-root PATH] [--remote-root PATH]
|
|
1833
|
-
[--server] [--headless] [-k FILE:LINE|LINE] [--server-port SERVER_PORT] [-d] [--doc-text]
|
|
1890
|
+
[--server] [--headless] [-k FILE:LINE|LINE] [--server-port SERVER_PORT] [-d] [--doc-text] [--info]
|
|
1834
1891
|
[program] [args ...]
|
|
1835
1892
|
```
|
|
1836
1893
|
|
|
@@ -1857,6 +1914,14 @@ usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
|
|
|
1857
1914
|
| `--server` | Enable JSON-RPC server alongside TUI |
|
|
1858
1915
|
| `--headless` | JSON-RPC server only, no TUI |
|
|
1859
1916
|
| `--server-port PORT` | Server port (default: 8150) |
|
|
1917
|
+
| `--record FILE` | Record this TUI session's debugging actions to FILE (JSON-RPC lines) |
|
|
1918
|
+
| `--replay FILE` | Replay a recording headless, printing a transcript (no program argument) |
|
|
1919
|
+
| `--replay-tui FILE` | Replay a recording inside the TUI at the recorded pace, so you can watch it |
|
|
1920
|
+
| `--replay-quiet` | With `--replay-tui`: no per-action toasts (errors and the final summary still show) |
|
|
1921
|
+
| `--timing` | With `--replay`: reproduce the recorded pacing (`--replay-tui` always does) |
|
|
1922
|
+
| `--replay-interval S` | With `--replay`/`--replay-tui`: fixed S-second delay before each action instead of the recorded gaps |
|
|
1923
|
+
| `--replay-timeout S` | With `--replay`/`--replay-tui`: per-action stop-wait timeout (default 30) |
|
|
1924
|
+
| `--examine-log DEST` | With --run: write each Ctrl-\ / SIGUSR2 stack snapshot to DEST (- = stdout; repeatable) |
|
|
1860
1925
|
|
|
1861
1926
|
## Configuration
|
|
1862
1927
|
|
|
@@ -1917,6 +1982,28 @@ fires as expected.
|
|
|
1917
1982
|
|
|
1918
1983
|
MIT
|
|
1919
1984
|
|
|
1985
|
+
## Acknowledgments
|
|
1986
|
+
|
|
1987
|
+
Thank you:
|
|
1988
|
+
|
|
1989
|
+
- Will McGugan for the amazing `textual` module.
|
|
1990
|
+
`tdb` would be a pale shadow of itself had I used any other TUI framework.
|
|
1991
|
+
Fantastic work, Will.
|
|
1992
|
+
|
|
1993
|
+
- Microsoft for the Debug Adapter Protocol (DAP) and releasing
|
|
1994
|
+
its implementation in `debugpy` and the Python Debugger extension for Visual Studio Code
|
|
1995
|
+
as open source.
|
|
1996
|
+
|
|
1997
|
+
- Anthropic, for providing access to Claude Code through the
|
|
1998
|
+
[Claude for Open Source](https://claude.com/contact-sales/claude-for-oss) program.
|
|
1999
|
+
`tdb` was made almost entirely with Claude Code.
|
|
2000
|
+
|
|
2001
|
+
- OpenAI, for providing access to Codex through the
|
|
2002
|
+
[Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
|
|
2003
|
+
|
|
2004
|
+
This project was inspired by Andreas Klöckner's excellent [pudb](https://pypi.org/project/pudb/)
|
|
2005
|
+
Python debugger.
|
|
2006
|
+
|
|
1920
2007
|
|
|
1921
2008
|
## Known Problems
|
|
1922
2009
|
|
|
@@ -47,7 +47,7 @@ It specifically supports modules
|
|
|
47
47
|
|
|
48
48
|
- supports remote attachment to Python, Perl, Ruby, Rust, C/C++, and Go programs
|
|
49
49
|
|
|
50
|
-
- includes a JSON-RPC server mode, an MCP mode, and a `SKILL.md` file that enable
|
|
50
|
+
- includes a JSON-RPC server mode, an MCP (v2) mode, and a `SKILL.md` file that enable
|
|
51
51
|
programmatic debug control, making it suitable for
|
|
52
52
|
automated, headless debugging workflows and AI-assisted debugging
|
|
53
53
|
|
|
@@ -61,28 +61,6 @@ to have `tdb` pop open automatically at the first uncaught exception
|
|
|
61
61
|
making it suitable for operation in non-graphical environments (mouse support is
|
|
62
62
|
available in graphical environments)
|
|
63
63
|
|
|
64
|
-
## Acknowledgments
|
|
65
|
-
|
|
66
|
-
Thank you:
|
|
67
|
-
|
|
68
|
-
- Will McGugan for the amazing `textual` module.
|
|
69
|
-
`tdb` would be a pale shadow of itself had I used any other TUI framework.
|
|
70
|
-
Fantastic work, Will.
|
|
71
|
-
|
|
72
|
-
- Microsoft for the Debug Adapter Protocol (DAP) and releasing
|
|
73
|
-
its implementation in `debugpy` and the Python Debugger extension for Visual Studio Code
|
|
74
|
-
as open source.
|
|
75
|
-
|
|
76
|
-
- Anthropic, for providing access to Claude Code through the
|
|
77
|
-
[Claude for Open Source](https://claude.com/contact-sales/claude-for-oss) program.
|
|
78
|
-
`tdb` was made almost entirely with Claude Code.
|
|
79
|
-
|
|
80
|
-
- OpenAI, for providing access to Codex through the
|
|
81
|
-
[Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
|
|
82
|
-
|
|
83
|
-
This project was inspired by Andreas Klöckner's excellent [pudb](https://pypi.org/project/pudb/)
|
|
84
|
-
Python debugger.
|
|
85
|
-
|
|
86
64
|
## Gallery
|
|
87
65
|
<p align="center">
|
|
88
66
|
<img src="https://github.com/AlDanial/tdb/blob/main/gallery/async_breakpoint.png" alt="at breakpoint" width="300">
|
|
@@ -122,6 +100,10 @@ uvx --from textual-debugger tdb my_program.py
|
|
|
122
100
|
# show comprehensive documentation in a terminal-based Markdown viewer
|
|
123
101
|
tdb --doc
|
|
124
102
|
|
|
103
|
+
# show where tdb is installed, where its bundled Perl PadWalker module
|
|
104
|
+
# lives, and the Help > About text
|
|
105
|
+
tdb --info
|
|
106
|
+
|
|
125
107
|
# debug a script (stops at first line by default)
|
|
126
108
|
tdb my_program.py
|
|
127
109
|
|
|
@@ -269,7 +251,7 @@ non-standard location, or change a language's default adapter, add to
|
|
|
269
251
|
Core debugging works identically for every language: breakpoints (incl.
|
|
270
252
|
conditions and persistence), stepping, continue/pause, run-to-cursor, stack
|
|
271
253
|
navigation, variable inspection, the evaluate console, syntax highlighting,
|
|
272
|
-
and the JSON-RPC / MCP programmatic modes.
|
|
254
|
+
and the JSON-RPC / MCP (v2) programmatic modes.
|
|
273
255
|
|
|
274
256
|
Python-specific features are hidden or return "not supported for this
|
|
275
257
|
language" message when debugging other languages: statement-granularity
|
|
@@ -473,6 +455,24 @@ metadata for broader primitives and stronger ownership evidence.
|
|
|
473
455
|
`perl` ≥ 5.18 on `PATH`. It drives stock `perl5db` under the
|
|
474
456
|
hood, so it works with any Perl already on the system.
|
|
475
457
|
|
|
458
|
+
The [PadWalker](https://metacpan.org/pod/PadWalker) module v2.5, by Robin Houston,
|
|
459
|
+
is bundled with `tdb`. PadWalker provides a more complete view of lexical variables
|
|
460
|
+
in outer/caller frames and therefore provides a richer debugging experience.
|
|
461
|
+
PadWalker is an XS (compiled) module, so the bundled copy has to be built
|
|
462
|
+
against the exact perl that runs your program. `tdb` does this for you on the
|
|
463
|
+
first Perl launch: if the perl in use cannot already load PadWalker (from CPAN
|
|
464
|
+
or a distro package), `tdb` compiles the bundled sources, caches the result
|
|
465
|
+
per interpreter under its config directory (`~/.config/tdb/padwalker/` on
|
|
466
|
+
Linux/macOS, `%APPDATA%\\tdb\\padwalker\\` on Windows), and prepends that
|
|
467
|
+
directory to the debuggee's `PERL5LIB`. The build takes about a second and
|
|
468
|
+
needs perl's headers (`libperl-dev` on Debian/Ubuntu, `perl-devel` on
|
|
469
|
+
Fedora/RHEL) plus a C compiler; Strawberry Perl on Windows ships both. When
|
|
470
|
+
the build is not possible, `tdb` prints a one-line notice on the console and
|
|
471
|
+
falls back to a read-only pad walk, so outer-frame lexicals degrade but
|
|
472
|
+
debugging otherwise proceeds. `tdb --info` reports the source directory, the
|
|
473
|
+
cache directory, and whether a build exists for the `perl` on your `PATH`.
|
|
474
|
+
`TDB_PADWALKER_CACHE` overrides the cache location.
|
|
475
|
+
|
|
476
476
|
**Launching a script:**
|
|
477
477
|
|
|
478
478
|
```bash
|
|
@@ -569,13 +569,6 @@ export PERL5LIB=/opt/tdb-perl:$PERL5LIB
|
|
|
569
569
|
the two files in the same relative layout shown above; `helpers.pl` is a
|
|
570
570
|
sibling of the `Devel/` directory, not inside it.)
|
|
571
571
|
|
|
572
|
-
**PadWalker (optional but recommended):** inspecting lexical (`my`)
|
|
573
|
-
variables in the *current* frame always works. Lexicals in outer/caller
|
|
574
|
-
frames need the `PadWalker` module installed on the debuggee's Perl; without
|
|
575
|
-
it, tdb falls back to a read-only pad walk that can't reach fully into
|
|
576
|
-
enclosing scopes, and outer-frame variable listings degrade accordingly.
|
|
577
|
-
Install with `cpanm PadWalker` (or your distro's package) for full fidelity.
|
|
578
|
-
|
|
579
572
|
**Pause is unavailable in attach mode.** Launch-mode sessions (`tdb
|
|
580
573
|
script.pl`) support pausing a running program at any time. Remote-attach
|
|
581
574
|
sessions don't. Asynchronous pause (as Python gets via `debugpy`) needs a control channel
|
|
@@ -1449,6 +1442,54 @@ available (breakpoints, stepping, variable inspection, the evaluate console). Th
|
|
|
1449
1442
|
debuggee itself never receives the signal (`tdb`'s adapter runs in its own process
|
|
1450
1443
|
group), so its own `SIGINT` handling is undisturbed.
|
|
1451
1444
|
|
|
1445
|
+
**Examining without opening the TUI:** press `Ctrl-\` in the terminal (`Ctrl-Break` on
|
|
1446
|
+
Windows), or, on Unix, send `SIGUSR2` to `tdb`'s pid: `kill -USR2 <tdb pid>`.
|
|
1447
|
+
|
|
1448
|
+
**Windows caveat:** the Ctrl-Break trigger is untested. Unlike Ctrl-C, a console
|
|
1449
|
+
Ctrl-Break is delivered to every process attached to the console, including the debug
|
|
1450
|
+
adapter and the program itself, so it may terminate the program instead of snapshotting
|
|
1451
|
+
it. There is no signal-based alternative on Windows.
|
|
1452
|
+
|
|
1453
|
+
`tdb` pauses the program, writes one JSON line describing the call stack of every thread
|
|
1454
|
+
(plus asyncio tasks and multiprocessing children for Python, goroutines for Go, and
|
|
1455
|
+
the concurrency snapshot for Rust), and resumes the program. Ctrl-C behavior is
|
|
1456
|
+
unchanged. By default the line goes to stdout; `--examine-log DEST` sends it to a file
|
|
1457
|
+
instead (`-` means stdout; repeat the flag to write to several places):
|
|
1458
|
+
|
|
1459
|
+
```bash
|
|
1460
|
+
tdb --run --examine-log hang.jsonl my_program.py # file only, appended
|
|
1461
|
+
tdb --run --examine-log - --examine-log hang.jsonl my_program.py # both
|
|
1462
|
+
```
|
|
1463
|
+
|
|
1464
|
+
Each record looks like (abridged):
|
|
1465
|
+
|
|
1466
|
+
```json
|
|
1467
|
+
{"schema":1,"seq":1,"trigger":"SIGQUIT","status":"ok",
|
|
1468
|
+
"requested_at":"2026-09-07T14:02:11.482-07:00","landed_at":"2026-09-07T14:02:11.511-07:00",
|
|
1469
|
+
"elapsed_s":87.3,"language":"python","program":"/home/al/work/app.py",
|
|
1470
|
+
"processes":[{"pid":41213,"role":"parent","name":null,
|
|
1471
|
+
"threads":[{"id":1,"name":"MainThread","frames":[
|
|
1472
|
+
{"function":"wait","file":"/usr/lib/python3.13/threading.py","line":359},
|
|
1473
|
+
{"function":"main","file":"/home/al/work/app.py","line":42}]}],
|
|
1474
|
+
"tasks":[{"name":"worker-2","state":"PENDING","awaiting":"Lock.acquire","frames":["run (/home/al/work/app.py:18)"]}]}],
|
|
1475
|
+
"errors":[]}
|
|
1476
|
+
```
|
|
1477
|
+
|
|
1478
|
+
Frames are innermost first. `tasks` appears only for Python programs with live asyncio
|
|
1479
|
+
tasks; `goroutines` only for Go; `rust_concurrency` only for Rust. `status` is
|
|
1480
|
+
`"pending"` when the pause could not land within a few seconds (the program is blocked
|
|
1481
|
+
inside a single call) -- a second record with the same `seq` and `status: "ok"` follows
|
|
1482
|
+
when it does land. If you press Ctrl-C or the program exits before it lands, no
|
|
1483
|
+
completing record is written. `errors` lists any sub-collector that failed, so a partial
|
|
1484
|
+
record is explicit about what is missing. A stderr line confirms each capture and where
|
|
1485
|
+
it went.
|
|
1486
|
+
|
|
1487
|
+
Narrow a long capture with `jq`, for example only threads with a frame in your own code:
|
|
1488
|
+
|
|
1489
|
+
```bash
|
|
1490
|
+
jq -c '.processes[].threads[] | select(any(.frames[]; .file != null and (.file|test("/work/"))))' hang.jsonl
|
|
1491
|
+
```
|
|
1492
|
+
|
|
1452
1493
|
**Quitting an adopted session** adds a third choice to the usual quit dialog:
|
|
1453
1494
|
- `d` (or `q`) -- detach and resume: the program keeps running headlessly; interrupt it
|
|
1454
1495
|
again later the same way.
|
|
@@ -1473,7 +1514,7 @@ language.
|
|
|
1473
1514
|
blocking external call or syscall, the pause can't land until that call returns --
|
|
1474
1515
|
`tdb` prints a notice and opens the TUI once the stop actually arrives.
|
|
1475
1516
|
|
|
1476
|
-
**Incompatible flags:** `-r`, `-k`/`-t`, `--record`, `--replay`, `--server`,
|
|
1517
|
+
**Incompatible flags:** `-r`, `-k`/`-t`, `--record`, `--replay`, `--replay-tui`, `--server`,
|
|
1477
1518
|
`--headless`, `--mcp`, `--terminal`.
|
|
1478
1519
|
|
|
1479
1520
|
### Eval Mode (`-e`/`--eval`)
|
|
@@ -1516,7 +1557,7 @@ evaluated in each child process that reaches it.
|
|
|
1516
1557
|
the eval point matches that bound line, so the expression still runs.
|
|
1517
1558
|
|
|
1518
1559
|
**Incompatible flags:** `--run`, `-r`, `-a`, `-k`/`-t`, `--record`,
|
|
1519
|
-
`--replay`, `--server`, `--headless`, `--mcp`, `--terminal`.
|
|
1560
|
+
`--replay`, `--replay-tui`, `--server`, `--headless`, `--mcp`, `--terminal`.
|
|
1520
1561
|
|
|
1521
1562
|
### Keybinding Schemes
|
|
1522
1563
|
|
|
@@ -1638,14 +1679,29 @@ written to `session.jsonl` as
|
|
|
1638
1679
|
JSON-RPC commands. Works with launch mode (any language) and `-r`
|
|
1639
1680
|
remote attach.
|
|
1640
1681
|
|
|
1641
|
-
Replay it
|
|
1642
|
-
|
|
1682
|
+
Replay it three ways:
|
|
1683
|
+
|
|
1684
|
+
- `tdb --replay-tui session.jsonl` opens the normal TUI on the recorded
|
|
1685
|
+
program and performs every recorded action in front of you, at the
|
|
1686
|
+
recorded pace: breakpoints appear in the Code View, steps and continues
|
|
1687
|
+
move the cursor, Evaluate entries echo into the console with their
|
|
1688
|
+
results, stack navigation and variable expansion play out in their
|
|
1689
|
+
panels. The title bar shows progress (`tdb ▶ session.jsonl 3/11`) and a
|
|
1690
|
+
toast announces each action (`--replay-quiet` turns those toasts off).
|
|
1691
|
+
A failed action raises an error toast and replay continues. When the recording does not end with `quit`, the
|
|
1692
|
+
debugger stays open at the final stop for you to carry on by hand.
|
|
1693
|
+
`--replay-interval S` waits a fixed S seconds before each action
|
|
1694
|
+
instead of reproducing the recorded gaps. `--replay-timeout S` bounds
|
|
1695
|
+
each stop-wait (default 30 s). Exit code 0
|
|
1696
|
+
iff every action succeeded. Don't type into the TUI while it is
|
|
1697
|
+
replaying: stray keystrokes interleave with the recorded actions.
|
|
1643
1698
|
- `tdb --replay session.jsonl` launches the recorded
|
|
1644
1699
|
program headless, feeds every recorded command through the same RPC
|
|
1645
1700
|
dispatch `tdb --server` uses, and prints a transcript (recorded time,
|
|
1646
1701
|
command, verbatim result, interleaved program output). Exit code 0 iff
|
|
1647
1702
|
every command succeeded. Add `--timing` to reproduce the original
|
|
1648
|
-
pacing
|
|
1703
|
+
pacing or `--replay-interval S` for a fixed delay before each command,
|
|
1704
|
+
`--replay-timeout S` to bound each stop-wait (default 30 s).
|
|
1649
1705
|
- Against a live server: start `tdb --server prog.py`, then feed line 2
|
|
1650
1706
|
onward of the file to `POST /rpc` . Each line is a valid request body:
|
|
1651
1707
|
|
|
@@ -1662,9 +1718,10 @@ lists), breakpoint enable/disable toggles, variable expansions when the
|
|
|
1662
1718
|
adapter reports no `evaluateName` (currently the Perl adapter), and
|
|
1663
1719
|
File > Open program switches.
|
|
1664
1720
|
|
|
1665
|
-
## MCP Integration
|
|
1721
|
+
## MCP v2 Integration
|
|
1666
1722
|
|
|
1667
|
-
tdb ships a Model Context Protocol (MCP)
|
|
1723
|
+
tdb ships a Model Context Protocol (MCP), version 2,
|
|
1724
|
+
server (`tdb-mcp`) that exposes
|
|
1668
1725
|
the debugger as a curated set of tools an AI agent can call. The MCP
|
|
1669
1726
|
server is a third in-process consumer of the same dispatch handlers the
|
|
1670
1727
|
TUI and the HTTP server use so an agent gets the same lock semantics,
|
|
@@ -1762,7 +1819,7 @@ usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
|
|
|
1762
1819
|
[--keybindings {default,vim,emacs}]
|
|
1763
1820
|
[--terminal {xterm,konsole,gnome-terminal,ghostty,kitty,iterm2,warp,wezterm,terminator}]
|
|
1764
1821
|
[--local-root PATH] [--remote-root PATH]
|
|
1765
|
-
[--server] [--headless] [-k FILE:LINE|LINE] [--server-port SERVER_PORT] [-d] [--doc-text]
|
|
1822
|
+
[--server] [--headless] [-k FILE:LINE|LINE] [--server-port SERVER_PORT] [-d] [--doc-text] [--info]
|
|
1766
1823
|
[program] [args ...]
|
|
1767
1824
|
```
|
|
1768
1825
|
|
|
@@ -1789,6 +1846,14 @@ usage: tdb [-h] [-v] [-r [HOST:]PORT] [--cwd CWD] [--no-stop-on-entry]
|
|
|
1789
1846
|
| `--server` | Enable JSON-RPC server alongside TUI |
|
|
1790
1847
|
| `--headless` | JSON-RPC server only, no TUI |
|
|
1791
1848
|
| `--server-port PORT` | Server port (default: 8150) |
|
|
1849
|
+
| `--record FILE` | Record this TUI session's debugging actions to FILE (JSON-RPC lines) |
|
|
1850
|
+
| `--replay FILE` | Replay a recording headless, printing a transcript (no program argument) |
|
|
1851
|
+
| `--replay-tui FILE` | Replay a recording inside the TUI at the recorded pace, so you can watch it |
|
|
1852
|
+
| `--replay-quiet` | With `--replay-tui`: no per-action toasts (errors and the final summary still show) |
|
|
1853
|
+
| `--timing` | With `--replay`: reproduce the recorded pacing (`--replay-tui` always does) |
|
|
1854
|
+
| `--replay-interval S` | With `--replay`/`--replay-tui`: fixed S-second delay before each action instead of the recorded gaps |
|
|
1855
|
+
| `--replay-timeout S` | With `--replay`/`--replay-tui`: per-action stop-wait timeout (default 30) |
|
|
1856
|
+
| `--examine-log DEST` | With --run: write each Ctrl-\ / SIGUSR2 stack snapshot to DEST (- = stdout; repeatable) |
|
|
1792
1857
|
|
|
1793
1858
|
## Configuration
|
|
1794
1859
|
|
|
@@ -1849,6 +1914,28 @@ fires as expected.
|
|
|
1849
1914
|
|
|
1850
1915
|
MIT
|
|
1851
1916
|
|
|
1917
|
+
## Acknowledgments
|
|
1918
|
+
|
|
1919
|
+
Thank you:
|
|
1920
|
+
|
|
1921
|
+
- Will McGugan for the amazing `textual` module.
|
|
1922
|
+
`tdb` would be a pale shadow of itself had I used any other TUI framework.
|
|
1923
|
+
Fantastic work, Will.
|
|
1924
|
+
|
|
1925
|
+
- Microsoft for the Debug Adapter Protocol (DAP) and releasing
|
|
1926
|
+
its implementation in `debugpy` and the Python Debugger extension for Visual Studio Code
|
|
1927
|
+
as open source.
|
|
1928
|
+
|
|
1929
|
+
- Anthropic, for providing access to Claude Code through the
|
|
1930
|
+
[Claude for Open Source](https://claude.com/contact-sales/claude-for-oss) program.
|
|
1931
|
+
`tdb` was made almost entirely with Claude Code.
|
|
1932
|
+
|
|
1933
|
+
- OpenAI, for providing access to Codex through the
|
|
1934
|
+
[Codex for Open Source](https://developers.openai.com/community/codex-for-oss) program.
|
|
1935
|
+
|
|
1936
|
+
This project was inspired by Andreas Klöckner's excellent [pudb](https://pypi.org/project/pudb/)
|
|
1937
|
+
Python debugger.
|
|
1938
|
+
|
|
1852
1939
|
|
|
1853
1940
|
## Known Problems
|
|
1854
1941
|
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"pygments>=2.17.0",
|
|
28
28
|
"fastapi>=0.115.0",
|
|
29
29
|
"uvicorn>=0.30.0",
|
|
30
|
-
"mcp>=
|
|
30
|
+
"mcp>=2.0.0,<3.0.0",
|
|
31
31
|
]
|
|
32
32
|
|
|
33
33
|
# extras needed to run the examples and tests
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
include = ["tdb", "tdb.*"]
|
|
61
61
|
|
|
62
62
|
[tool.setuptools.package-data]
|
|
63
|
-
tdb = ["README.md", "adapters/perl/helpers.pl", "adapters/perl/Devel/TdbRemote.pm", "adapters/perl/Devel/TdbCompile.pm", "adapters/bash/tdb_harness.sh", "adapters/powershell/tdb_launch.ps1", "rust_concurrency/probes/gdb_script.py", "rust_concurrency/probes/lldb_script.py"]
|
|
63
|
+
tdb = ["README.md", "adapters/perl/helpers.pl", "adapters/perl/padwalker_src/PadWalker.pm", "adapters/perl/padwalker_src/PadWalker.xs", "adapters/perl/padwalker_src/Makefile.PL", "adapters/perl/Devel/TdbRemote.pm", "adapters/perl/Devel/TdbCompile.pm", "adapters/bash/tdb_harness.sh", "adapters/powershell/tdb_launch.ps1", "rust_concurrency/probes/gdb_script.py", "rust_concurrency/probes/lldb_script.py"]
|
|
64
64
|
|
|
65
65
|
[tool.pytest.ini_options]
|
|
66
66
|
testpaths = ["tests"]
|