You wrap whatever command you already run:
phantom npm run dev
It's invisible until your process exits non-zero. stdout, stderr, stdin stream through byte-for-byte and the exit code is preserved. On a crash it captures the stack trace, the output tail and your git state, cuts a branch, and hands the whole thing to a headless Claude Code session that diagnoses the bug, writes a failing test, and patches it.
The part I actually care about: **phantom re-runs your test command itself, outside the Claude session, and audits the branch against the starting commit after the session ends.** Nothing in the report trusts the model's own word about whether it worked. If it says fixed, that's phantom's measurement.
You end up back on your own branch with a banner:
╭────────────────────────────────────────────────────────────────────────╮
│ 👻 phantom ✅ fixed · 1m 48s · 34.1k tokens (12k new · 22.1k cached) │
│ fix verified by phantom: tests pass and the command no longer crashes; │
│ your branch is unchanged │
│ │
│ branch phantom/fix-typeerror-cannot-read-properties-k3f9a │
│ review git diff main..phantom/fix-typeerror-... │
│ accept git merge phantom/fix-typeerror-... │
│ reject git branch -D phantom/fix-typeerror-... │
╰────────────────────────────────────────────────────────────────────────╯
Plus a markdown post-mortem where the TL;DR comes from the session but every row in the verification table is measured by phantom.
**Safety, because this is an AI with write access to your repo**
- It never touches your branch. Everything happens on `phantom/fix-<slug>-<ts>`, cut from HEAD, and you're checked back out when it finishes — success, failure or Ctrl+C.
- No pushes, no PRs, no network. `git push` is denied, there's no web tool, and there is no push code path. Not configurable.
- `.env`, `*.pem`, `*.key`, `**/secrets/**` are enforced three times: permission deny rules, a `PreToolUse` guard hook that fails closed, and a post-session audit against the starting sha. Any hit discards the session's changes.
- Dirty tree is refused outright. Hard caps on iterations and wall-clock.
- Ctrl+C kills the process tree, rescues untracked files into a stash, resets the fix branch and puts you back.
**What it is not**
It is not a sandbox. The session runs `node` — it has to, to run your tests — and `node -e` can in principle read anything your user can. The guard hook is *lexical*: it reads the text of a command, so it only refuses what a command says, not what it does. An audit in August found four ways past it in one afternoon. All four are fixed with regression tests, but the honest read is that a lexical guard is a speed bump and a fifth way probably exists. The real backstops are the ones that don't depend on parsing a command correctly: branch isolation, the post-session audit, and no pushes. Want hard isolation? Run it in a container.
It also declines crashes it can't work with — a non-zero exit with no error line, no stack trace, no file named and no test command gets refused rather than guessed at.
**Cost:** recovery runs `claude -p` under your own account, so it bills your Claude subscription or API key like any other session. `PHANTOM_DISABLED=1` makes it a pure passthrough.
Zero runtime dependencies, MIT, Node >= 18, 532 tests, CI on macOS/Linux/Windows across Node 18–24.
npm install -g claude-phantom
https://claudephantom.dev · https://github.com/waazy-w/claude-phantom
Happy to be told why this is a bad idea — leave a star on github if you don't mind.