Lazydap
Lazydap
A scriptable, terminal-first debugger. CLI core, JSON-over-Unix-socket protocol, multiple frontends. Wraps DAP adapters (codelldb / debugpy / etc.) so any AI agent, shell script, or custom UI can drive a debug session.
Project lives at: ~/code/planetaryescape/lazydap/
Architecture inherited from: ~/code/planetaryescape/mxr/
Quick links into the project
- [
/code/planetaryescape/lazydap/README.md](file:///Users/bhekanik/code/planetaryescape/lazydap/README.md) — entry point - [
/code/planetaryescape/lazydap/ARCHITECTURE.md](file:///Users/bhekanik/code/planetaryescape/lazydap/ARCHITECTURE.md) — high-level architecture - [
/code/planetaryescape/lazydap/AGENTS.md](file:///Users/bhekanik/code/planetaryescape/lazydap/AGENTS.md) — agent guidance - [
/code/planetaryescape/lazydap/CLAUDE.md](file:///Users/bhekanik/code/planetaryescape/lazydap/CLAUDE.md) — Claude Code-specific - [
/code/planetaryescape/lazydap/TODO.md](file:///Users/bhekanik/code/planetaryescape/lazydap/TODO.md) — milestone progress docs/blueprint/00-overview.md— what + why + scopedocs/blueprint/14-roadmap.md— milestone plandocs/blueprint/15-decision-log.md— every decision + rationaledocs/articles/the-cli-is-the-product.md— the positioning thesisdocs/articles/agent-driven-debugging.md— competitive analysis + research findingsdocs/articles/yes-its-a-wrapper.md— answers "isn't this just a wrapper on DAP?". Landing-page-quality positioning copy.
Status
Pre-alpha. No code yet. The ~/code/planetaryescape/lazydap/ directory currently holds the full architecture, blueprint, and per-milestone implementation plan. Code arrives starting with milestone M0.
Milestones M0–M18, organised in 5 phases:
- A — see the protocol (M0–M4): raw DAP plumbing, no UI
- B — daemon + protocol (M5–M7): daemon, IPC, CLI subcommands, agent skill
- C — TUI (M8–M11): ratatui shell, Elm architecture, IPC-wired
- D — useful features → v0.1 (M12–M15): stack pane, scopes, breakpoint UI, config
- E — beyond v0.1 (M16–M18): watches, REPL, second adapter
The architecture in one paragraph
Single binary lazydap with subcommands. Auto-spawning daemon owns the DAP adapter and live session. Clients (TUI, CLI, agent skill, anything) talk to it over a length-delimited JSON Unix socket. Scriptability is the core tenet: every operation goes through the protocol, no client gets privileged access. Inherits mxr's discipline: strict crate boundaries enforced by Cargo, CLI-first culture, JSON output as a product feature, --dry-run for mutations, tracing from start, real-system tests not mocks.
Tech stack
- Rust — sticking with what I'm learning
ratatui(plain) — TUI rendering. Hand-rolled Elm Architecture for state. Not Iocraft, not tui-realm — too many novel concepts at oncecrossterm— terminal backendtokio— async runtimeserde+serde_json— JSON serialisationtracing— structured logging from line 1 ofmain
Decisions made (full list in 15-decision-log.md)
- TOML state files (
.lazydap/state.toml), not SQLite. Scriptable from any language, version-controllable. - Multi-session designed for from M5 (every IPC message has session_id), enforced N=1 in v0.1. Lifting later is daemon-only change.
.vscode/launch.jsonparsed from M5+. Drop-in usable in existing repos.- Same
.skillZIP shape as mxr (SKILL.md+references/commands.md). - One daemon per project, keyed by repo root.
--waitis the bridge from async DAP to sync shell. 30s default timeout. Buffers intervening events. Coalesces 50ms for additional thread stops. (Full design indocs/blueprint/10-async-to-sync.md.)- AI features are external clients of the protocol. Core ships two primitives (
Subscribe { channels }events +getStateSnapshotrich JSON), community ships AI features. (Seedocs/blueprint/12-ai-future.md.)
Research findings — what already exists
(Full article: docs/articles/agent-driven-debugging.md.)
The gap is real. Every existing AI-debug solution is MCP-tied (assumes Claude/Cursor host), VS-Code-tied, or DAP-tied. Nothing exposes debugging as plain shell subcommands.
Direct competitors:
debugmcp/mcp-debugger(TypeScript, ~100 stars) — closest in spirit, MCP-onlyGovinda-Fichtner/debugger-mcp(Rust, early-stage)go-delve/mcp-dap-server— reference for async-to-sync bridgingjasonjmcghee/claude-debugs-for-you(~507 stars) — VS Code-tiedmicrosoft/DebugMCP,VS Code Copilot Debug Agent— VS Code-tiedlldb-dap,dlv dap,gdb -i dap— speak DAP, not subcommands
Research validates the demand: Microsoft's debug-gym reports +30% (Claude 3.7), +182% (o1), +160% (o3-mini) on SWE-bench Lite when LLMs have pdb available. Runtime debugger access genuinely helps.
Lazydap's positioning: shell-first, JSON output, no MCP runtime needed, TUI parity. Narrow, sharp.
Future AI roadmap (post-v0.1)
(Full version: docs/blueprint/12-ai-future.md.)
Two primitives in core; everything else external:
- Streaming events API (already in v0.1)
getStateSnapshot— rich JSON for one-shot LLM context
Tier 1 (v0.2): "why did this break?", stack trace summarisation, auto-watch suggestion. Single LLM call each.
Tier 2 (v0.3): generate regression test from paused state, "fix it" patch suggestion.
Tier 3 (v0.4+): "set bps to find this bug", autonomous debug agent.
Closest research analogues: ChatDBG, InspectCoder, debug-gym.
Learning resources
Curated reading/watching path. Read DAP first, watch ratatui videos, study nvim-dap as the closest reference architecture, then start M0.
1. DAP — start here (read, not watch)
The protocol is small enough that 60–90 minutes of reading beats any video.
- DAP overview — start here, 10 min read
- DAP specification — full spec, ~50 message types, treat as reference
- DAP overview.md on GitHub — same content, slightly different phrasing
- Building a Debug Adapter (DeepWiki) — reverse angle (writing an adapter), clarifies the lifecycle
- Python DAP client reference impl — small, clean reference. Read this to see what a DAP client looks like before writing one in Rust
- DAP Magic — Modern Debugging Experience (Medium) — high-level explainer with diagrams
2. ratatui — the videos to watch
This is where the YouTube content is genuinely good.
- Ratatui Tutorial Beginners Guide — covers layouts, widgets, events. Best starting point.
- Rust TUI Tutorial: Ratatui, Multithreading, Responsiveness — directly relevant to lazydap's hardest problem (async DAP layer + sync TUI rendering)
- Super awesome Terminal UIs (TUIs) in Rust using RATATUI.rs — composable component patterns
- Ratatui official tutorials — written, project-based (JSON Editor walkthrough is a good warm-up)
- Ratatui homepage + crate docs
2.5. Lua primer (prerequisite for reading nvim-dap source)
You'll need just enough Lua to read nvim-dap. Not a Lua guru — just tables, closures, the : syntax, modules, pcall, and coroutines (the load-bearing concept for nvim-dap's async-to-sync bridge).
→ See Lua Primer for Reading nvim-dap for the curated 3-hour path + cheat sheet.
3. nvim-dap walkthroughs — closest reference architecture
These show DAP-in-action at the user level. Even though it's Lua, the session lifecycle and event handling map 1:1 to what you'll build.
- Debugging In NeoVim V0.11 With nvim-dap — June 2025, covers C++ which is your case
- Debugging in Neovim is easy, actually — June 2025, broader setup tour
- How to configure Debuggers in Neovim — practical config walkthrough
- Debugging In Neovim (ft BashBunni) — Go-focused, patterns universal
- Neovim Debugging with NVIM-DAP — solid 101
- Articles: John Tobin: Debugging in Neovim, Tamerlan: A Guide to Debugging Code in Neovim, Miguel Crespo: Debug like a PRO, eliasdorneles TIL
4. Optional — "build a debugger from scratch"
You're NOT building this (DAP adapters do it for you), but skimming one of these helps understand what the adapter is actually doing on the other side of the wire.
- Writing a Debugger from Scratch — DbgRs (Tim Misiak) — Rust, Windows usermode. Excellent series. Read just part 1 for context.
- Writing a Debugger (joekain) — C and Rust, Linux x86-64, ptrace-based
- Forum announcement of "Writing a Debugger in Rust" — index of resources
5. Reference projects to study (architecture, not docs)
mfussenegger/nvim-dap—lua/dap/session.luais the closest reference for session lifecycle. Lua but the patterns translate.igorlfs/nvim-dap-view— closest to target aesthetic. Single-pane tabbed UI.tomlin7/debug-adapter-client(Python) — well-structured DAP client decoupled from transport. Cleanest small reference.go-delve/mcp-dap-server—tools.gocontinue handler is the closest existing analogue to lazydap's--wait. Read it.jesseduffield/lazygit— for TUI architecture, key handling, modal UX.~/code/planetaryescape/mxr— own project; reference for daemon-backed CLI architecture, crate boundaries, IPC contract.
6. Async-to-sync DAP — research findings
(Detailed analysis in docs/blueprint/10-async-to-sync.md.)
Key references for the --wait design:
- DAP spec: stable state =
stoppedORterminated(with optionalexited) - nvim-dap async-to-sync model: Lua coroutines + event listeners.
Session:requestlines 2130–2163 insession.lua. State mutations need generation counters to avoid races (issue #1365). - mcp-dap-server pattern: loop reading messages until
StoppedEventorTerminatedEvent. No timeout; we add 30s default. - VS Code: has NO
wait-for-stable-stateprimitive. vscode#78663 is the open feature request. - LLDB pattern:
lldb.SBProcess.Continue() + WaitForEventForBroadcasterWithType(timeout, ...). Explicit timeout. Lazydap mirrors this. - GDB pattern: pwntools
continue_and_waitvscontinue_nowait— useful naming distinction.
Suggested reading order
- Day 1: read DAP overview + spec (1–2 hours)
- Day 2: skim Python
debug-adapter-clientrepo to see the shape of a real client - Day 3–7: watch the ratatui beginner video + work through the official JSON Editor tutorial
- Day 8+: read
nvim-dap'ssession.lua, then start [M0](file:///Users/bhekanik/code/planetaryescape/lazydap/docs/implementation/tasks/M00-hello-adapter.md)
Tip: when stuck on DAP semantics, read the actual JSON traffic. nvim-dap can be told to log every message:
require("dap").set_log_level("TRACE")
-- log goes to ~/.cache/nvim/dap.log
Reading 10 minutes of real DAP traffic teaches more than any blog post.
Internal-only sibling
Pre-M0 questions, things-I'm-watching-for during M0–M5, betting-against/betting-on, and architecture intuitions to test in code live in Lazydap Personal Notes (deliberately not published to the garden).
Related vault notes
Connects to: LangX (own language design project), Mxr (sister project, same architecture), my interest in Rust generally, the broader question of "what would the terminal-first IDE look like?".
Vault clusters this project touches
If you're reading this in a year and want to traverse, lazydap connects to these synthesis notes (each opens its own cluster):
- Debugger mechanics → How Debuggers Actually Work — DAP, adapters, ptrace, DWARF, INT3 breakpoints, native vs managed. lazydap is the agent-friendly wrapper sitting at the top of this stack.
- Daemon architecture → How Daemons Work — auto-spawn, PID files, signals, IPC server. lazydap's daemon mirrors mxr's pattern.
- IPC plumbing → How Processes Talk to Each Other — Unix sockets + length-prefixed JSON. The wire lazydap's clients ride.
- TUI state management → The Elm Architecture (TEA) — what lazydap's TUI uses internally (hand-rolled, per blueprint decision D012).
- Client-agnostic cores → Client-Agnostic Cores — the architectural pattern lazydap embodies (along with Mxr).
Atomic notes on debugging concepts (the most relevant cluster)
- How Debuggers Actually Work — synthesis: full layer cake from your editor to the kernel
- Debug Adapter Protocol (DAP) — the IDE ↔ adapter wire format
- DAP Adapter — bridges DAP to a real debugger
- ptrace — Linux syscall every native debugger relies on
- DWARF Debug Symbols — the source-to-machine-code map
- Software Breakpoints — the INT3 patching trick
- Native vs Managed Debugging — radically different mechanisms, same DAP on top
- Plumbing and Porcelain — the metaphor that makes the layered stack comprehensible