{"_id":"@aboosoyeed/dap-cli","name":"@aboosoyeed/dap-cli","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@aboosoyeed/dap-cli","version":"0.1.0","type":"module","description":"CLI Debug Adapter Protocol client for LLM agents and automation","main":"dist/index.js","types":"dist/index.d.ts","bin":{"dap-cli":"dist/cli.js"},"scripts":{"dev":"bun run src/cli.ts","build":"bun build src/cli.ts --outdir dist --target node --format esm && bun build src/index.ts --outdir dist --target node --format esm","build:tsc":"tsc","prepublishOnly":"bun run build && bun run build:tsc","typecheck":"tsc --noEmit","start":"node dist/cli.js","start:bun":"bun run src/cli.ts","test":"bun run tests/runner.ts","test:js":"bun run tests/runner.ts js-node","test:ts":"bun run tests/runner.ts ts-node","test:bun":"bun run tests/runner.ts ts-bun","test:python":"bun run tests/runner.ts python","test:rust":"bun run tests/runner.ts rust","test:verbose":"bun run tests/runner.ts --verbose"},"keywords":["debugger","dap","debug-adapter-protocol","cli","llm","automation"],"author":"","license":"MIT","dependencies":{"@vscode/debugadapter":"^1.65.0","@vscode/debugprotocol":"^1.65.0","commander":"^12.0.0","source-map":"^0.7.6","tar":"^7.5.7","ws":"^8.19.0"},"devDependencies":{"@types/node":"^20.0.0","@types/ws":"^8.18.1","typescript":"^5.3.0"},"gitHead":"4184b3852ef0881a48772e3d733f0a8dccdf459c","_id":"@aboosoyeed/dap-cli@0.1.0","_nodeVersion":"24.11.1","_npmVersion":"11.6.2","dist":{"integrity":"sha512-kaa2Hg3SzhHHkzv6NeYzIT+otgg8IeTPITxZ2F51tfpC2bGWpbBI1MiDvDgqWukd/jnU8WDEDC52Uc9Z1ekjYg==","shasum":"18e706c3f225005ddbc7b33ac593bdbe6b7fd4d1","tarball":"https://registry.npmjs.org/@aboosoyeed/dap-cli/-/dap-cli-0.1.0.tgz","fileCount":44,"unpackedSize":1118777,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCx0INHCt9ePj7NOXuqXSkRP3e7+Q4tSToQte6FUsoO4wIgNQNE5RoVzOvnv8KjBe+/zbkgKJQoSWvGITLHVgaz0aQ="}]},"_npmUser":{"name":"aboosoyeed","email":"soyeed2000@gmail.com"},"directories":{},"maintainers":[{"name":"aboosoyeed","email":"soyeed2000@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dap-cli_0.1.0_1787975598527_0.1351506781034355"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-29T03:53:18.329Z","0.1.0":"2026-08-29T03:53:18.666Z","modified":"2026-08-29T03:53:18.913Z"},"maintainers":[{"name":"aboosoyeed","email":"soyeed2000@gmail.com"}],"description":"CLI Debug Adapter Protocol client for LLM agents and automation","keywords":["debugger","dap","debug-adapter-protocol","cli","llm","automation"],"license":"MIT","readme":"# dap-cli\n\nA command-line debugger for LLM agents and automation. Set breakpoints, run code, get JSON.\n\n## Quick Start\n\n```bash\n# Debug Node.js - no setup required\ndap-cli run --breakpoint src/api.ts:42 -- node src/index.js\n\n# Debug Bun - no setup required\ndap-cli run --breakpoint src/api.ts:42 -- bun src/index.ts\n\n# Debug TypeScript with Node\ndap-cli run --breakpoint src/api.ts:42 -- node --loader ts-node/esm src/index.ts\n\n# Debug Python (requires: dap-cli setup python)\ndap-cli run --breakpoint app.py:10 --adapter python -- python3 app.py\n\n# Debug Rust (requires: dap-cli setup rust)\ndap-cli run --breakpoint src/main.rs:10 --adapter rust -- cargo run\n```\n\n**Output:**\n```json\n{\n  \"breakpoint\": {\n    \"file\": \"src/api.ts\",\n    \"line\": 42,\n    \"hit\": true\n  },\n  \"variables\": {\n    \"userId\": null,\n    \"documentId\": \"abc-123\",\n    \"request\": \"[Object]\"\n  },\n  \"callStack\": [\n    {\"name\": \"createDocument\", \"file\": \"src/api.ts\", \"line\": 42},\n    {\"name\": \"handleRequest\", \"file\": \"src/index.ts\", \"line\": 15}\n  ]\n}\n```\n\n## Problem\n\nLLM agents debug code using `console.log` because:\n- Debuggers are interactive/GUI-based (VS Code, Chrome DevTools)\n- No CLI tool outputs debug state as structured JSON\n- Results in wasteful cycles: add log → run → parse → repeat\n\n## Solution\n\nA CLI tool that sets breakpoints and outputs JSON:\n\n```bash\ndap-cli run --breakpoint \"src/routes/documents.ts:42\" -- node server.js\n```\n\n## Installation\n\n### Requirements\n\n- Node.js 18+ or Bun 1.0+\n- macOS, Linux, or Windows\n\n### From Source\n\n```bash\ngit clone https://github.com/aboosoyeed/dap-cli.git\ncd dap-cli\n\n# Install dependencies\nbun install\n\n# Run directly (development)\nbun run src/cli.ts run --breakpoint file.ts:10 -- node file.js\n\n# Or build and link globally\nbun run build\nbun link\n```\n\n## Usage\n\n### Basic Debugging\n\n```bash\n# Debug Node.js\ndap-cli run --breakpoint src/index.ts:42 -- node src/index.js\n\n# Debug Bun\ndap-cli run --breakpoint src/index.ts:42 -- bun src/index.ts\n\n# Multiple breakpoints (hits first one)\ndap-cli run \\\n  --breakpoint src/api.ts:10 \\\n  --breakpoint src/db.ts:25 \\\n  -- node app.js\n```\n\n### TypeScript Support\n\nTypeScript files are automatically compiled and source-mapped:\n\n```bash\n# Node.js with TypeScript (source maps handled automatically)\ndap-cli run --breakpoint src/api.ts:42 -- node dist/api.js\n\n# Bun runs TypeScript directly\ndap-cli run --breakpoint src/api.ts:42 -- bun src/api.ts\n```\n\n### Supported Runtimes\n\n| Runtime | Setup Required | Protocol |\n|---------|---------------|----------|\n| **Node.js** | None | Chrome DevTools Protocol (CDP) |\n| **Bun** | None | WebKit Inspector Protocol |\n| **TypeScript** | None | Automatic source map translation |\n| **C#/.NET** | `dap-cli setup csharp` | Debug Adapter Protocol (netcoredbg) |\n| **Python** | `dap-cli setup python` | Debug Adapter Protocol (debugpy) |\n| **Rust** | `dap-cli setup rust` | Debug Adapter Protocol (CodeLLDB) |\n\n### Bun DAP Adapter (Optional)\n\nFor enhanced Bun debugging, you can install the official DAP adapter:\n\n```bash\ndap-cli setup bun\n```\n\nThis provides better breakpoint support but is optional - the default WebKit mode works without setup.\n\n### C# / .NET (netcoredbg)\n\nC# debugging requires the netcoredbg adapter:\n\n```bash\ndap-cli setup csharp\ndap-cli run --breakpoint Program.cs:20 --adapter csharp -- dotnet run --project MyApp.csproj\n```\n\n### Python (debugpy)\n\nPython debugging requires debugpy, installed into a dap-cli-managed directory\n(not your project's environment) via `pip install --target`:\n\n```bash\ndap-cli setup python\ndap-cli run --breakpoint app.py:10 --adapter python -- python3 app.py\ndap-cli run --breakpoint app.py:10 --adapter python -- python3 -m myapp.cli\n```\n\n`--adapter python` is optional if the command starts with `python`/`python3`\n(auto-detected). Supports `--count`, `--trigger`/readiness, and\n`--break-on-exception` (Python's `uncaught` filter) with full parity to Node.\n\n### Rust (CodeLLDB)\n\nRust debugging requires CodeLLDB, downloaded from its GitHub releases:\n\n```bash\ndap-cli setup rust\ndap-cli run --breakpoint src/main.rs:10 --adapter rust -- cargo run\ndap-cli run --breakpoint src/bin/worker.rs:5 --adapter rust -- cargo run --bin worker\n```\n\n`--adapter rust` is optional if the command starts with `cargo` (auto-detected\nfor `cargo run`/`cargo build`); a prebuilt binary path needs `--adapter rust`\nexplicitly, since there's no runtime-name signal to detect it from. Supports\n`--count`, `--trigger`/readiness, and `--break-on-exception` (Rust panics, via\nCodeLLDB's `rust_panic` filter).\n\n## Debugging Long-Lived Services\n\nA server doesn't reach a breakpoint on its own — something has to send it a\nrequest. Instead of backgrounding dap-cli and curling from a second terminal,\nlet dap-cli launch the service, detect when it's ready, and fire the request\nitself, all in one command:\n\n```bash\ndap-cli run \\\n  --breakpoint src/server.ts:42 \\\n  --ready-pattern \"listening on\" \\\n  --trigger \"curl -s http://localhost:3000/users/42\" \\\n  -- node server.js\n```\n\ndap-cli launches the server, waits until it's ready, runs the `--trigger`\ncommand to drive execution into the handler, and captures the breakpoint.\n\n**Readiness detection** (how dap-cli knows the service is up before triggering):\n\n| Flag | Ready when… |\n|------|-------------|\n| `--ready-pattern <regex>` | the program's output matches (e.g. `\"listening on\"`) |\n| `--ready-port <port>` | the TCP port starts accepting connections |\n| `--ready-delay <ms>` | a fixed delay elapses (default 1000ms if `--trigger` has no pattern/port) |\n\nThe trigger runs **concurrently** with the breakpoint wait, so a request that\nblocks on the paused server is expected — its result shows `\"exitCode\": null`\nwith a note, which is the normal success shape:\n\n```json\n\"trigger\": { \"command\": \"curl -s http://localhost:3000/users/42\", \"exitCode\": null, \"error\": \"still running when session ended\" }\n```\n\n**Live output & heartbeat:** while waiting, the debuggee's stdout/stderr are\nstreamed to dap-cli's stderr (stdout stays pure JSON), and a\n`[dap-cli] waiting for breakpoint (12s/30s)` heartbeat shows it isn't hung.\nPass `--quiet` to suppress both.\n\n**Timeouts:** `--timeout` is the whole-session budget (readiness + breakpoint\nwait share it). Internal debugger protocol calls use a fixed short timeout, so\nraising `--timeout` for a slow service doesn't also mask a wedged connection.\n\n## Multiple Snapshots per Run\n\nDebugging is rarely one question. `--count N` captures up to N breakpoint hits\nin a single session (one server boot), auto-continuing between them — works on\nboth `run` and `attach` for Node, Python, and Rust (Bun/C# are single-hit for\nnow):\n\n```bash\ndap-cli run --count 3 --breakpoint src/handler.ts:20 -- node app.js\n```\n\nWith `--count > 1` the output is **NDJSON** — one compact JSON object per hit,\nstreamed as each hit occurs, plus a final summary line:\n\n```\n{\"hitIndex\":1,\"breakpoint\":{...},\"variables\":{\"i\":0},...}\n{\"hitIndex\":2,\"breakpoint\":{...},\"variables\":{\"i\":1},...}\n{\"summary\":{\"hitsCaptured\":2,\"requested\":5,\"reason\":\"program-exited\"}}\n```\n\n`reason` is `completed` (got all N), `timeout` (per-hit wait expired),\n`program-exited`, or `target-disconnected` (attach). Streaming NDJSON means a\nrun that ends early still leaves valid, parseable lines. `--timeout` applies\nper hit. `--count 1` (the default) keeps the original single-object output.\n\n## Attach to a Running Process\n\nFor a long-lived service, restarting it for every snapshot is expensive.\n`dap-cli attach` connects to an already-running process, captures a breakpoint\nhit, and **detaches without killing it** — leaving it resumed and free of your\nbreakpoints, on every exit path (including Ctrl-C).\n\n```bash\n# Node: start your server with --inspect, then attach\nnode --inspect server.js          # prints: Debugger listening on ws://...\ndap-cli attach --port 9229 --breakpoint src/server.js:42 \\\n  --trigger \"curl -s http://localhost:3000/users/42\"\n\n# Bun: attach to the ws:// URL bun prints (no --port; bun uses a random path)\nbun --inspect server.ts           # prints: ws://localhost:6499/<id>\ndap-cli attach --adapter bun --ws-url ws://localhost:6499/<id> --breakpoint server.ts:17\n\n# C#: attach by process id\ndap-cli attach --adapter csharp --pid 12345 --breakpoint Program.cs:22\n\n# Python: the target must call debugpy.listen() itself, then attach by port\npython3 -c \"import debugpy; debugpy.listen(5678); import app\"   # or add to your app\ndap-cli attach --adapter python --port 5678 --breakpoint app.py:10\n\n# Rust: attach by process id\ndap-cli attach --adapter rust --pid 12345 --breakpoint src/main.rs:20\n```\n\n`--trigger` works in attach mode too — it fires right after breakpoints are\narmed (no `--ready-*` flags needed, since an attach target is already\nrunning). The target is left running and responsive afterward — verified for\nNode, Bun, Python, and Rust.\n\n**Notes per runtime:**\n- **Node**: attach by `--port` (default 9229) or `--ws-url`.\n- **Bun**: `--ws-url` is required — Bun's inspector uses a random URL path and\n  has no discovery endpoint, so paste the URL bun prints.\n- **C#**: attach by `--pid`. netcoredbg attach is best-effort on macOS; on Linux\n  it may be blocked by `ptrace_scope` (run as the same user or with\n  `CAP_SYS_PTRACE`). Release/optimized builds may expose no locals — use Debug.\n- **Python**: attach by `--port` — the target process must already be running\n  `debugpy.listen(<port>)` (dap-cli connects directly to that socket; it\n  doesn't spawn a separate adapter for attach).\n- **Rust**: attach by `--pid`. Same ptrace/permission caveats as C# apply on\n  Linux; macOS may require Developer Mode or a codesigned debugger.\n\n## Failure Diagnostics\n\nWhen a run does not capture a snapshot, dap-cli prints a structured failure\nenvelope instead of an opaque error, so you can tell *why* the breakpoint\nwasn't hit:\n\n```json\n{\n  \"breakpoint\": { \"file\": \"src/server.ts\", \"line\": 42, \"hit\": false },\n  \"error\": { \"kind\": \"timeout\", \"message\": \"Timeout waiting for breakpoint after 5000ms\" },\n  \"unboundBreakpoints\": [{ \"file\": \"src/server.ts\", \"line\": 42 }],\n  \"output\": [\"listening on 4321\\n\"],\n  \"timestamp\": 1751500000000\n}\n```\n\nError `kind` is one of:\n\n| Kind | Meaning |\n|------|---------|\n| `timeout` | The breakpoint was never hit within the timeout (program still running) |\n| `child-exit` | The program finished or crashed before hitting a breakpoint (crash reason is in `message`) |\n| `breakpoint-file-not-found` | A breakpoint's file isn't on disk — usually a typo (fails immediately) |\n| `launch-failed` | The program couldn't be spawned/compiled, or the debugger never started |\n| `protocol-error` | The debugger connection died mid-session |\n| `internal` | Anything unclassified |\n\nThe captured program `output` is always included on failure — it usually\ncontains the real reason (a crash log, `EADDRINUSE`, wrong port). Unbound\nbreakpoints (set but never matched to executable code) are listed on timeout.\n\n**Exit codes:** `0` = snapshot captured; `2` = the tool ran fine but the debug\nobjective failed (`timeout` / `child-exit`) — the full JSON envelope is on\nstdout; `1` = a tool or usage error (`launch-failed`, `breakpoint-file-not-found`, …).\n\n## Programmatic API\n\n```typescript\nimport { runWithCdp } from 'dap-cli';\n\n// One-shot debugging - command is argv (never a shell-joined string)\nconst snapshot = await runWithCdp(\n  ['node', 'src/index.js', '--port', '3000'],\n  [{ file: 'src/api.ts', line: 42 }]\n);\n\nconsole.log(snapshot.variables);\nconsole.log(snapshot.callStack);\n```\n\n### Available Exports\n\n```typescript\n// High-level functions - launch and debug\nimport { runWithCdp } from 'dap-cli';      // Node.js debugging\nimport { runWithBun } from 'dap-cli';      // Bun debugging\nimport { runWithDap } from 'dap-cli';      // Generic DAP (Node-only, experimental)\nimport { runWithCSharp } from 'dap-cli';   // C#/.NET debugging\nimport { runWithPython } from 'dap-cli';   // Python debugging\nimport { runWithRust } from 'dap-cli';     // Rust debugging\n\n// High-level functions - attach to a running process\nimport { attachWithCdp } from 'dap-cli';       // Node.js attach\nimport { attachWithBun } from 'dap-cli';       // Bun attach\nimport { attachWithCSharp } from 'dap-cli';    // C#/.NET attach\nimport { attachWithPython } from 'dap-cli';    // Python attach\nimport { attachWithRust } from 'dap-cli';      // Rust attach\n\n// Low-level clients\nimport { CdpClient } from 'dap-cli';       // CDP client class\nimport { DapClient } from 'dap-cli';       // DAP client class (Node-only, experimental)\n\n// Generic DAP orchestrator - the shared engine behind Python and Rust; build\n// a new DAP-based adapter on it by implementing DapAdapterDescriptor\nimport { runWithDapSession, attachWithDapSession, DapSession } from 'dap-cli';\nimport type { DapAdapterDescriptor } from 'dap-cli';\n\n// Utilities\nimport { SourceMapper } from 'dap-cli';    // Source map handling\n\n// Types\nimport type { DebugSnapshot, Breakpoint, StackFrame } from 'dap-cli';\nimport type { DebugFailure, DebugErrorKind, MultiSnapshotResult, DebugSummary } from 'dap-cli';\nimport { DebugError } from 'dap-cli';      // Typed error thrown by run*/attach* on failure\n```\n\n## When This Tool Actually Helps\n\n**Honest assessment:** This tool helps with a specific class of bugs - not everything.\n\n### The Sweet Spot: \"Why is this value wrong?\"\n\n```\nTraditional debugging cycle:\n1. Add console.log(user)           → run → \"undefined\"\n2. Add console.log(getUserById)    → run → \"function exists\"\n3. Add console.log(userId)         → run → \"null\" ← finally found it\n4. Remove all the console.logs\n\nWith dap-cli:\n1. Set breakpoint at the suspicious line → run → see ALL variables at once\n   { \"userId\": null, \"user\": undefined, \"db\": \"[Object]\", ... }\n```\n\n**This reduces 4-7 iteration cycles to 1.**\n\n### Good Use Cases\n\n| Situation | Why it helps |\n|-----------|--------------|\n| \"Why is `user` null on line 42?\" | See all in-scope variables instantly |\n| \"What's actually in this request object?\" | Full variable inspection without serialization |\n| \"How did execution reach this point?\" | Call stack shows the full path |\n| \"What are the function arguments?\" | All parameters visible in snapshot |\n\n### When Console.log is Fine\n\n| Situation | Why dap-cli doesn't add much |\n|-----------|------------------------------|\n| Simple \"is this code running?\" checks | A log statement is simpler |\n| Logging across multiple runs | dap-cli is one-shot |\n| Async event sequences | Hard to capture with single breakpoint |\n| Type errors / syntax errors | This is runtime inspection only |\n\n### Realistic Example\n\nAn agent encounters a failing test:\n\n```\nError: Cannot read property 'email' of undefined\n    at sendWelcomeEmail (src/notifications.ts:45)\n```\n\n**Without dap-cli:**\n```typescript\n// Agent adds logs one by one\nconsole.log(\"user:\", user);           // run → undefined\nconsole.log(\"userId:\", userId);        // run → \"abc-123\"\nconsole.log(\"findUser result:\", ...);  // run → null\n// Finally understands: findUser is returning null\n```\n\n**With dap-cli:**\n```bash\ndap-cli run --breakpoint src/notifications.ts:45 -- npm test\n```\n```json\n{\n  \"variables\": {\n    \"userId\": \"abc-123\",\n    \"user\": undefined,\n    \"db\": \"[Database]\"\n  },\n  \"callStack\": [\n    {\"name\": \"sendWelcomeEmail\", \"file\": \"notifications.ts\", \"line\": 45},\n    {\"name\": \"createUser\", \"file\": \"users.ts\", \"line\": 23}\n  ]\n}\n```\n\nAgent immediately sees: `userId` exists but `user` is undefined → the lookup failed.\n\n### Bottom Line\n\nThis tool is most valuable when you **have a hypothesis** about where a bug might be and need to **validate it quickly**. It's not a replacement for understanding code - it's a faster way to test theories.\n\n## How It Works\n\n### Node.js (CDP)\n\n```\n┌─────────────┐     WebSocket      ┌─────────────────┐\n│   dap-cli   │ ◄────────────────► │    Node.js      │\n│             │   CDP Protocol     │  --inspect-brk  │\n└─────────────┘                    └─────────────────┘\n```\n\n1. Spawns Node.js with `--inspect-brk` (pauses on first line)\n2. Connects via Chrome DevTools Protocol\n3. Sets breakpoints, resumes execution\n4. Captures variables and call stack when breakpoint hits\n5. Outputs JSON and exits\n\n### Bun (WebKit)\n\n```\n┌─────────────┐     WebSocket      ┌─────────────────┐\n│   dap-cli   │ ◄────────────────► │       Bun       │\n│             │  WebKit Protocol   │  --inspect-brk  │\n└─────────────┘                    └─────────────────┘\n```\n\nSimilar flow but uses WebKit Inspector Protocol (JSC-based).\n\n## Limitations\n\n- **Bun**: breakpoints in the entry file (the script passed to `bun`) are translated through Bun's own source map and work anywhere in the file. Breakpoints in *imported* modules fall back to raw line matching, since those scripts haven't parsed yet when breakpoints are set - use `dap-cli setup bun` for full multi-file support\n- **Source Maps**: for Node.js, TypeScript breakpoints require compiled JS files with source maps present; for Bun, source maps are read inline from the running process, no compiled files needed\n- **Multi-hit**: `--count N` captures up to N hits (Node, Python, Rust); Bun/C# are single-hit for now\n- **Rust value rendering**: struct/enum fields beyond one level of nesting fall back to a raw LLDB summary string rather than a fully expanded object (same one-level-deep convention as the C#/Python adapters)\n\n## Development\n\n```bash\n# Run tests\nbun tests/runner.ts\n\n# Run specific test suite\nbun tests/runner.ts js-node\nbun tests/runner.ts ts-node\nbun tests/runner.ts ts-bun\nbun tests/runner.ts csharp   # requires: dotnet SDK + dap-cli setup csharp\nbun tests/runner.ts python   # requires: python3 + dap-cli setup python\nbun tests/runner.ts rust     # requires: cargo + dap-cli setup rust\nbun tests/runner.ts failure-modes\nbun tests/runner.ts trigger\nbun tests/runner.ts attach\nbun tests/runner.ts count\n\n# Build\nbun run build\n\n# Type check\nbun run typecheck\n```\n\n## References\n\n- [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/specification)\n- [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/)\n- [WebKit Inspector Protocol](https://webkit.org/web-inspector/)\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-ee219f6de0507e69b49a90680f73c8d0"}