A CLI tool that enables AI agents to programmatically debug code via the Debug Adapter Protocol (DAP).
Agents can write and run code, but when something goes wrong, they're blind. They can only see error messages and stack traces—they can't inspect actual runtime state. Human developers reach for the debugger; agents should too.
debug-run exposes debugging capabilities through structured JSON output that agents can parse and act on.
- Multi-language support: .NET, Python, Node.js, C/C++/Rust (via LLDB)
- Breakpoints: Line, conditional (
file:line?condition), hit count (file:line#count) - Exception breakpoints: Break on thrown/uncaught exceptions
- Variable inspection: Automatic recursive expansion of locals and
this - Expression evaluation: Evaluate arbitrary expressions at breakpoints
- Assertion-based debugging: Declare invariants that halt on violation
- Trace mode: Automatically step through execution paths
- Stepping: Step over, step into, step out with state capture
- Logpoints: Log messages without breaking execution
- Attach mode: Debug running processes
- Structured output: NDJSON event stream for easy parsing
# Install globally
npm install -g debug-run
# Or run directly with npx
npx debug-run --helpTo enable AI coding assistants to use debug-run effectively, install the skill:
# Install for Claude Code (default)
npx debug-run install-skill
# Install for GitHub Copilot
npx debug-run install-skill --copilot
# Install for both Claude Code and GitHub Copilot
npx debug-run install-skill --claude --copilot
# Install to project directory (for team sharing)
npx debug-run install-skill --project # Claude: .claude/skills/
npx debug-run install-skill --copilot --project # Copilot: .github/skills/
# Install to custom directory
npx debug-run install-skill --dir /path/to/skills| Option | Description |
|---|---|
--claude |
Install to Claude Code (~/.claude/skills/) - default |
--copilot |
Install to GitHub Copilot (~/.copilot/skills/) |
--project |
Install to project directory instead of user home |
--dir <path> |
Install to custom directory |
This copies skill files teaching AI assistants how to use debug-run for debugging .NET, Python, and TypeScript applications:
SKILL.md- Main skill with options reference and best practicesDOTNET.md- .NET-specific guide (vsdbg, ASP.NET, NUnit)PYTHON.md- Python-specific guide (debugpy)TYPESCRIPT.md- TypeScript/JavaScript guide (js-debug)
Note: For GitHub Copilot, enable the chat.useAgentSkills setting in VS Code for Agent Skills support.
git clone https://github.com/Chris-Cullins/debug-run.git
cd debug-run
npm install
npx tsx ./src/index.ts --help # run from sourcenpx debug-run list-adaptersnpx debug-run ./bin/Debug/net8.0/MyApp.dll \
-a dotnet \
-b "src/OrderService.cs:45" \
--prettynpx debug-run ./main.py \
-a python \
-b "processor.py:123" \
-e "data.count" \
--prettynpx debug-run ./dist/index.js \
-a node \
-b "src/handler.ts:30" \
--prettyUsage: debug-run [options] [command] [program]
Arguments:
program Program to debug
Options:
-a, --adapter <name> Debug adapter (dotnet, python, node, lldb)
--args <args...> Arguments to pass to the program
--cwd <path> Working directory for the program
-b, --breakpoint <spec...> Breakpoint specs (file:line, file:line?cond, file:line#count)
-e, --eval <expr...> Expressions to evaluate at breakpoints
--assert <expr...> Invariant expressions; stops on first violation
-l, --logpoint <spec...> Logpoints (file:line|message with {expr})
--break-on-exception <filter...> Break on exceptions (all, uncaught, user-unhandled)
-t, --timeout <duration> Session timeout (default: 60s)
--capture-locals Capture local variables (default: true)
--pretty Pretty print JSON output
-s, --steps <count> Steps to execute after breakpoint hit
--capture-each-step Capture state at each step
--trace Enable trace mode - step through code
--trace-into Use stepIn instead of stepOver in trace
--trace-limit <N> Max steps in trace mode (default: 500)
--trace-until <expr> Stop trace when expression is truthy
--attach Attach to running process
--pid <id> Process ID to attach to
--env <key=value...> Environment variables
--compact Enable compact output for reduced token usage
--stack-limit <N> Max stack frames to include (default: 3 in compact)
Commands:
list-adapters List available debug adapters
install-adapter <name> Install a debug adapter
# Simple line breakpoint
-b "src/file.cs:45"
# Conditional breakpoint (break when condition is true)
-b "src/file.cs:45?order.Total > 1000"
# Hit count breakpoint (break on Nth hit)
-b "src/file.cs:45#3"
# Logpoint (log without breaking)
-l "src/file.cs:45|Processing order {order.Id} with total {order.Total}"debug-run outputs newline-delimited JSON (NDJSON) events:
{"type":"session_start","timestamp":"...","adapter":"dotnet","program":"./app.dll"}
{"type":"breakpoint_set","id":1,"file":"src/Service.cs","line":45,"verified":true}
{"type":"process_launched","timestamp":"..."}
{"type":"breakpoint_hit","id":1,"threadId":1,"location":{...},"locals":{...},"evaluations":{...}}
{"type":"process_exited","exitCode":0}
{"type":"session_end","summary":{"breakpointsHit":1,"duration":1234}}When a breakpoint cannot be verified (e.g., source mapping issues, missing debug symbols), the event includes actionable diagnostics:
{
"type": "breakpoint_set",
"id": 1,
"file": "src/handler.ts",
"line": 45,
"verified": false,
"message": "Could not resolve source location",
"diagnostics": {
"requestedFile": "src/handler.ts",
"requestedLine": 45,
"adapterMessage": "Could not resolve source location",
"suggestions": [
"Ensure \"sourceMap\": true in tsconfig.json",
"Rebuild with source maps: tsc --sourceMap (or your build command)",
"Verify .map files exist in your output directory (e.g., dist/**/*.map)"
],
"adapterType": "node",
"fileExtension": ".ts"
}
}Suggestions are context-aware based on the adapter and file type.
{
"type": "breakpoint_hit",
"id": 1,
"threadId": 1,
"timestamp": "2025-01-15T10:30:01.234Z",
"location": {
"file": "src/OrderService.cs",
"line": 45,
"function": "ProcessOrder"
},
"stackTrace": [...],
"locals": {
"order": {
"type": "OrderDto",
"value": {
"Id": "abc-123",
"Total": 150.00,
"Items": {"type": "List<Item>", "count": 3, "items": [...]}
}
},
"this": {...}
},
"evaluations": {
"order.Items.Count": {"result": "3", "type": "int"}
}
}| Adapter | Languages | Installation |
|---|---|---|
dotnet / vsdbg |
C#, F#, VB.NET | VS Code C# extension (auto-detected) |
netcoredbg |
C#, F#, VB.NET | debug-run install-adapter netcoredbg |
python / debugpy |
Python | pip install debugpy |
node |
JavaScript, TypeScript | VS Code (js-debug built-in) |
lldb |
C, C++, Rust, Swift | Xcode CLI tools or LLVM |
$ npx debug-run list-adapters
Available debug adapters:
dotnet
ID: coreclr
Status: ✓ installed (/path/to/vsdbg)
debugpy
ID: python
Status: ✓ installed (python3)
node
ID: pwa-node
Status: ✗ not installed
lldb
ID: lldb
Status: ✗ not installednpx debug-run ./bin/Debug/net8.0/TestApp.dll \
-a dotnet \
-b "src/InventoryService.cs:34" \
-e "requestedQuantity" \
-e "availableStock" \
--prettynpx debug-run ./app.dll \
-a dotnet \
-b "src/PricingService.cs:45" \
--steps 10 \
--capture-each-step \
--prettynpx debug-run ./app.dll \
-a dotnet \
--break-on-exception "all" \
--prettynpx debug-run ./app.dll \
-a dotnet \
-b "src/OrderService.cs:67?order.Total > 1000" \
--prettyDeclare invariants that must remain true. The debugger halts immediately when any assertion fails:
npx debug-run ./app.dll \
-a dotnet \
-b "src/OrderService.cs:45" \
--assert "order.Total >= 0" \
--assert "order.Items.Count > 0" \
--assert "customer != null" \
--prettyWhen an assertion fails, you get an assertion_failed event with:
- The failed assertion expression
- The actual value
- Full stack trace and locals
- Location where it failed
Assertions are checked at breakpoints, during stepping, and during trace mode.
Reduce token usage by 40-60% with compact output:
npx debug-run ./app.dll \
-a dotnet \
-b "src/OrderService.cs:45" \
--compact \
--prettyCompact mode applies these optimizations:
- Stack trace limiting: Only top 3 frames by default (configurable with
--stack-limit) - Internal frame filtering: Removes node_modules, runtime internals, webpack frames
- Path abbreviation:
~/project/src/file.tsinstead of/Users/name/project/src/file.ts - Variable diffing: On repeated breakpoint hits, only reports changed variables (
_diffkey) - Trace path collapsing: Consecutive identical locations collapsed to
functionName (x5)
Example compact output vs verbose:
// Compact (~60 tokens)
{"type":"breakpoint_hit","location":{"file":".../src/OrderService.cs","line":45,"function":"ProcessOrder"},"stackTrace":[{"function":"ProcessOrder","file":".../src/OrderService.cs","line":45}],"locals":{"order":{"type":"OrderDto","value":{"Id":"abc-123","Total":150}}}}
// Verbose (~180 tokens)
{"type":"breakpoint_hit","timestamp":"2025-01-15T10:30:01.234Z","id":1,"threadId":1,"location":{"file":"/home/user/project/src/OrderService.cs","line":45,"column":12,"function":"ProcessOrder","module":"MyApp"},"stackTrace":[{"frameId":1,"function":"ProcessOrder","file":"/home/user/project/src/OrderService.cs","line":45,"column":12,"module":"MyApp"},{"frameId":2,"function":"Main","file":"/home/user/project/src/Program.cs","line":10,"column":5},...],"locals":{"order":{"type":"OrderDto","value":{"Id":"abc-123","Total":150,"CreatedAt":"2025-01-15T00:00:00Z","Status":"pending",...}},"this":{...}}}Automatically step through code after hitting a breakpoint:
npx debug-run ./app.dll \
-a dotnet \
-b "src/OrderService.cs:45" \
--trace \
--trace-limit 100 \
--prettyTrace mode options:
--trace: Enable trace mode--trace-into: Follow into function calls (default: step over)--trace-limit <N>: Max steps before stopping (default: 500)--trace-until <expr>: Stop when expression becomes truthy
debug-run is designed for AI agents to use programmatically:
import subprocess
import json
result = subprocess.run([
"npx", "debug-run",
"./bin/Debug/net8.0/MyApp.dll",
"-a", "dotnet",
"-b", "src/Service.cs:45",
"-e", "order.Total",
"-t", "30s"
], capture_output=True, text=True)
for line in result.stdout.strip().split('\n'):
event = json.loads(line)
if event['type'] == 'breakpoint_hit':
print(f"Hit breakpoint at {event['location']['file']}:{event['location']['line']}")
print(f"Locals: {event['locals']}")# Install dependencies
npm install
# Run from source
npx tsx ./src/index.ts [args...]
# Type check
npm run typecheck
# Build
npm run build
# Run tests
npm testsrc/
├── index.ts # Entry point
├── cli.ts # Command-line parsing
├── dap/ # DAP client implementation
│ ├── client.ts # Main DAP client
│ ├── transport.ts # Content-Length framing
│ └── protocol.ts # DAP message types
├── session/ # Debug session management
│ ├── manager.ts # Session lifecycle
│ ├── variables.ts # Variable inspection
│ └── breakpoints.ts
├── adapters/ # Debug adapter configurations
│ ├── base.ts # Adapter interface
│ ├── debugpy.ts # Python
│ ├── node.ts # Node.js
│ ├── lldb.ts # LLDB
│ └── ...
└── output/ # Event formatting
MIT