# pybreakz

A zero-dependency Python debugger CLI built for Claude Code.

Run your script with breakpoints, capture locals and stack state, get clean structured output — all in one shot. No interactive session required.

---

## Install

```bash
pip install pybreakz
```

Or install from source:

```bash
git clone https://github.com/allannapier/py_debug_cli.git
cd py_debug_cli
pip install -e .
```

Requires Python 3.9+. No pip dependencies.

---

## Usage

### Basic breakpoints

```bash
pybreakz run script.py --breakpoints 42
pybreakz run script.py --breakpoints 10,25,42
```

Captures all locals at each breakpoint hit.

### Watch specific expressions

```bash
pybreakz run script.py --breakpoints 42 --watch "user_id,response.status,len(items)"
```

Evaluates each expression in the breakpoint's local scope.

### Evaluate arbitrary expressions

```bash
pybreakz run script.py --breakpoints 42 --eval "df.shape,type(result),items[:3]"
```

Like `--watch` but separate section in output — useful for computed values.

### Capture on exception

```bash
pybreakz run script.py --on-exception
pybreakz run script.py --on-exception --watch "self,request,data"
```

Captures locals, stack, and full traceback at the crash site.

### Conditional breakpoints

```bash
pybreakz run script.py --breakpoints "42:x>100,67:status=='error'"
```

Only fires when the condition evaluates to True.

### Pass arguments to your script

```bash
pybreakz run script.py --breakpoints 10 -- --input data.csv --verbose
```

Anything after `--` is forwarded to the script as `sys.argv`.

### JSON output (for programmatic use)

```bash
pybreakz run script.py --breakpoints 42 --format json
```

### Timeout

```bash
pybreakz run script.py --breakpoints 10 --timeout 60   # 60s limit
pybreakz run script.py --breakpoints 10 --timeout 0    # no limit
```

Default timeout is 30 seconds.

---

## Example output

```
════════════════════════════════════════════════════════════
  pybreakz report  →  script.py
════════════════════════════════════════════════════════════

┌─ BREAKPOINT HIT #1  script.py:42
│  Source:      result = process(item)
│
│  Call Stack (innermost last):
│  <module>()  [script.py:80]
│  main()  [script.py:60]
│  run_batch()  [script.py:42]
│
│  Locals:
│    items                = list[150 items]: [{'id': 1, ...}, ...]
│    item                 = {'id': 1, 'name': 'foo', 'active': True}
│    result               = None
│  Watched:
│    item['id']           = 1
│    len(items)           = 150
└────────────────────────────────────────────────────────────
```

---

## How Claude Code uses it

Claude Code treats `pybreakz` like any other shell command. A typical debugging loop:

1. Claude reads your code and identifies suspicious lines
2. Calls `pybreakz run script.py --breakpoints 34,67 --watch "x,response"`
3. Reads the report output
4. Adjusts breakpoints or patches the bug
5. Repeats if needed

The `--on-exception` flag is especially useful as a first pass — just run it and let the crash tell Claude where to look.

---

## Options reference

| Flag | Short | Description |
|------|-------|-------------|
| `--breakpoints LINES` | `-b` | Comma-separated line numbers, optionally with conditions (`42:x>0`) |
| `--watch EXPRS` | `-w` | Comma-separated expressions to evaluate at each hit |
| `--eval EXPRS` | `-e` | Additional expressions to evaluate (shown separately) |
| `--on-exception` | `-x` | Capture state on unhandled exception |
| `--timeout SECS` | `-t` | Script timeout in seconds (default: 30) |
| `--format FORMAT` | `-f` | Output format: `text` (default) or `json` |
| `--max-hits N` | | Max breakpoint hits to record (default: 20) |

---

## Limitations

- Works on `.py` scripts run directly. Not designed for long-running servers or async event loops.
- Breakpoints must be on **executable lines** (not blank lines, comments, or `def`/`class` headers — use the first line inside the function body).
- `sys.settrace` has a small performance overhead — not suitable for tight performance benchmarks.
- Multi-threaded scripts: only the main thread is traced.
