Skip to main content

👻 GhostPatch

An AI software engineer that understands how your code connects.

Describe a bug, or let it find one. GhostPatch maps the codebase into a live graph, finds the root cause, writes the fix, works out everything the change could break, and proves it: the new tests fail without the fix and pass with it.

Python Languages Tests Models License

The GhostPatch dashboard after a fix tournament: on the left, the result, a red-green proof (2 failed without the fix, 2 passed with it), the tournament table where candidate 1 beat candidate 2 on its rival's tests, and a 100/100 verification score; on the right, the code graph with the edited function in mint and its blast radius in red, and the diff below.

A two-fix tournament on a checkout bug. The winner fixed the root cause and passed its rival's tests; the loser patched the symptom in the cart. The fix is proven red→green, and the poltergeist couldn't break it.


The problem

AI coding agents are good at writing code, but most of them explore a repository the way a newcomer would: by searching text and opening files one at a time. They find the line that looks wrong, patch it, and move on, without knowing what else depends on that line.

That is how a "fix" to a discount calculation quietly changes wholesale invoicing too.

The idea

GhostPatch gives the agent a map before it touches anything.

It parses the codebase into a graph of files, classes, functions, tests and who-calls-what, keeps that graph in sync as files change, and puts it at the centre of the agent's work:

  • Calls are linked through imports, module aliases, self/this and the types the code states (cart *Cart, Cart cart, let cart = Cart::new()), so the graph knows which total() a call means, not just that something called total.
  • Before the first step, GhostPatch ranks the code most likely at fault (names and words from the report, quoted messages found in the code, then along the call graph towards the cause) and puts the top suspects' source in the agent's first message. On the benchmark, the buggy function is among those suspects in 31 of 31 cases (first in 18), before the model has read a thing.
  • The agent starts every task with an outline of the whole repository.
  • It can ask structural questions: where is this defined, who calls it, which tests cover it?
  • Every edit is automatically followed by an impact report: what depends on the changed function, how far the change ripples, and exactly which tests to run. The agent gets this whether or not it thought to ask. That matters most with smaller, free models.

Proof, not promises

An AI saying "fixed, and the tests pass" is a claim. GhostPatch checks it itself.

  • 🛡 Regression guard. GhostPatch runs the whole test suite before the agent starts and again after the fix. A test that passed before and fails now goes straight back to the agent ("your fix broke test_average"), up to twice. A fix that still breaks something is never called fixed.
  • 🔴→🟢 Red-green proof. After every fix, GhostPatch runs the new tests twice: with the fix taken back out (they must fail) and with it in place (they must pass). Tests that pass either way prove nothing, and it says so. If the agent wrote no test, a test-only step writes one first, so nearly every fix gets proven.
  • 🏆 Fix tournament. With --candidates 3, independent fixes compete, each with its own strategy: direct, test first, or graph first. They are judged on evidence: their proofs, their confidence scores, and cross-examination, where every fix must also pass its rivals' tests. A patch that only fixes the reported symptom loses to one that fixes the root cause.
  • 👻 Poltergeist. A second agent that may only write tests then tries to break the winner.
  • The proof ships with the pull request: the red and green test results, the confidence score and the tournament table are in its description.

It hunts bugs while you sleep

  • Haunt mode (ghostpatch haunt) finds bugs nobody has reported. It ranks every function by risk (how many places call it, whether any test reaches it, how often its file changed lately) and sends a haunter that may only write tests at the riskiest ones. GhostPatch runs those tests itself, and a skeptic, a separate model call with no stake in the claim, must agree that a failing test reflects what the code is meant to do before the bug counts. Every confirmed bug comes with its failing test as proof, and one click fixes it.
  • Night shift (ghostpatch nightshift) works through every open issue labelled ghostpatch. It opens a pull request for each fix it can prove, rolls back the ones it can't, haunts for new bugs, and leaves a morning report. On GitHub Actions it runs every night for free.
  • Label an issue, get a pull request. With the GitHub Action, adding the ghostpatch label to an issue is all it takes.

Haunt mode: the riskiest functions ranked with their reasons; a confirmed bug in apply_discount with its failing test and the skeptic's verdict, and two clean functions; the graph marks the bug in red. Ask mode: an answer to how checkout computes the total, citing files and lines, the call flow as a tree, and the same flow highlighted on the code graph.

Left: haunt mode found the coupon bug without being told about it. Right: ask mode answers from the code and draws the real call flow.

Ask it anything

ghostpatch ask "How does checkout calculate the total?" answers from the code with a read-only agent that cites path:line. GhostPatch then draws the call flow between the functions the answer mentions, taken from the code graph rather than the model's memory, and highlights it in the dashboard.

See it work

GhostPatch has been tested end to end on three demo projects, using free models:

Bug Language What GhostPatch did Steps Cost
average() returns the wrong mean Python Found the off-by-one slice, fixed it, added 3 tests 9 $0
A 10% coupon charges customers $0.00 Python, 5 files Traced checkout → cart → pricing, fixed / 10 → / 100, flagged that bulk invoicing shares the function, added coupon tests 9–13 $0
Buying 3 mugs only charges for 1 TypeScript Fixed the subtotal, flagged the free-shipping rule as affected, added a regression test, ran node --test 7 $0

What the agent sees right after its edit in the second case, generated by the code graph:

🕸 Code graph: you changed shop.pricing.apply_discount.
Changing 'apply_discount' may affect:
  direct callers:
    shop/cart.py:15  in shop.cart.Cart.total
    shop/invoice.py:11  in shop.invoice.bulk_price
  callers of those:
    shop/checkout.py:7  in shop.checkout.checkout
  tests to run: tests/test_cart.py::test_checkout_receipt, tests/test_cart.py::test_total_without_coupon_adds_tax

Benchmark

bench/ holds 31 realistic bug cases (17 Python, 8 TypeScript, 2 each in Go, Rust and Java). Each is judged by hidden tests the agent never sees, and many are traps where fixing only the symptom fails: a helper shared by receipts and the CSV export, a function that quietly compensates for the bug, a shared default that must not be mutated. ghostpatch bench --validate checks that every case is sound (its hidden tests fail on the buggy code and pass with the reference fix).

Where to look first, measured without any model (ghostpatch bench --localize): the function the reference fix changes is GhostPatch's top suspect in 18/31 cases, in the top 3 in 29/31 and in the top 5 (whose source the agent receives) in 31/31. The six Go, Rust and Java cases were added after the ranker was written and measured without changing it: five rank the bug first.

End to end with a real model: the first 5 cases, on the free qwen/qwen3.8-27b via Groq (from before the regression guard and where-to-look were added):

Case with graph without graph
py-calculator ✅ 7 steps ✅ 6 steps
py-deep-merge (trap) ✅ 6 steps ✅ 6 steps
py-mutable-default ✅ 5 steps ✅ 7 steps
py-pagination ✅ 5 steps ✅ 6 steps
py-price-format (trap) ✅ 4 steps ✅ 7 steps
Solved 5/5 5/5

Both settings solved every case so far. With the graph the agent needed 16% fewer steps (27 vs 32) but used 8% more tokens (75.6k vs 69.9k), because the repository map and impact reports add context. The remaining cases run as the free daily quota allows: ghostpatch bench --compare resumes where it stopped, and ghostpatch bench --report prints the table.

ghostpatch bench --full judges GhostPatch's whole fixing session instead of the bare agent (where to look first, the regression guard, the regression-test writer and the proof). Its first real run solved py-count-compensated, a trap where a caller quietly compensates for the bug, in 6 steps; the other cases are waiting on the free quota.

The core loop

flowchart LR
    R([Bug report]) --> L[Rank suspects<br/>graph + report]
    L --> B[Baseline:<br/>whole test suite]
    B --> F[Agent fixes<br/>or a tournament]
    F --> G{Regression guard:<br/>anything broken?}
    G -- yes, back to the agent --> F
    G -- no --> T[Write a test<br/>if there is none]
    T --> P[Red-green proof]
    P --> C[Confidence score<br/>and saved run]

Features

🕸 Living code graph. Python, JavaScript, TypeScript, Go, Rust and Java are parsed into symbols and calls, stored in SQLite and re-indexed incrementally: only changed files are re-parsed. Tests are recognised the way each language marks them (JS/TS test("adds tax", () => …) blocks, Go TestXxx functions, Rust #[test] and #[cfg(test)] modules, JUnit @Test methods), so impact reports name real tests.

🧰 Every language's own tools. GhostPatch runs pytest, node --test / npm test, go test, cargo test, Maven and Gradle (or the project's mvnw / gradlew), reads which tests failed from each one's output for the regression guard, and runs just the new tests for the proof: by package in Go, by --test target or module in Rust, by class in Java. Rust unit tests usually sit in the file they test, so the proof takes the fix out of that file while keeping the new test in.

🤖 Autonomous agent loop. The agent explores, reproduces the bug, fixes the root cause, verifies it with the project's own tests and writes a summary. It has 15 tools, from read_file and replace_lines to impact_of_change and remember.

👻 Live dashboard. ghostpatch serve streams the agent's work to the browser as it happens: every file read, every edit as a diff, every test run. The code graph lights up in real time, and commands can be approved or denied with a click.

The Runs view: a history of runs with confidence scores, and a replay scrubber showing each step and the blast radius at that moment. The dashboard on a phone-sized screen.

The Insights view: the code graph at a past commit, with the function that commit added marked NEW, and a commit slider.

💸 Free by default. Works with Groq, OpenRouter, Google Gemini and local Ollama models at no cost, or OpenAI when you want more power. It is built for free-tier realities: it waits out per-minute limits, switches provider when a daily quota runs out, and shortens old tool output so each request stays small. Any other OpenAI-compatible server (Ollama on another machine, LM Studio, vLLM) works too: set GHOSTPATCH_BASE_URL.

⬆ GitHub-native. Point it at an issue link and it reads the issue, fixes it and opens a pull request that says Fixes #42, committing only its own changes on a fresh branch. It works through the GitHub CLI, so it never touches a GitHub token.

↩ Undo anything. Every run is recorded with the before-and-after content of each file it touched. ghostpatch undo, or one click in the dashboard, puts everything back, including deleting files the run created. It refuses to overwrite edits you made afterwards unless you insist.

More superpowers

Feature What it does
👻 Poltergeist mode After a fix, an adversarial agent that may only write tests tries to break it, armed with the diff and the fix's blast radius. If it succeeds, the ghost gets its failing tests and fixes the code again. --poltergeist
🧭 Crash-to-graph tracing Paste a Python, Node/TypeScript or Java stack trace, or a Go or Rust panic, and it is mapped onto the code graph: the ghost starts from the exact crash path, and the dashboard can animate it. ghostpatch trace
🩺 Blast-radius PR review For any change, including pull requests written by people: which functions changed, what else they affect, and which of those no test reaches. ghostpatch review 42 --post
🤖 CI auto-fixer When CI goes red, GhostPatch fixes the code, re-runs the tests itself, then opens a pull request. Ships as a GitHub Action. ghostpatch ci-fix
🎬 Replay and share Every run is recorded step by step. Export one as a single HTML page with a replay scrubber that anyone can open. ghostpatch share
🕳 Test-gap map Every function no test reaches, and one command to have the ghost write tests for them. ghostpatch gaps --write-tests 5
⏳ Architecture time-lapse The code graph at each of the last N commits, read straight from git, with what appeared and disappeared. ghostpatch timelapse
🔀 Free-model fallback When one provider's daily quota runs out mid-fix, it switches to the next free provider and carries on with the same conversation.
🧠 Repo memory Team conventions in GHOSTPATCH.md, plus facts the ghost learns as it works, fed into every run. ghostpatch memory
📊 Confidence score Every fix gets 0 to 100: did the tests pass after the last edit, and how much of the blast radius do tests actually reach?

🛡 Safe by design. File access is confined to the repository and nothing is committed or pushed. Commands follow an approval mode: ask (always ask), safe (recognised test and read-only git commands run automatically; anything that chains, redirects or substitutes commands still asks) or all (for sandboxes). The dashboard binds to 127.0.0.1, rejects cross-site requests and checks the Host header against DNS rebinding.

Architecture

flowchart LR
    U([Bug report]) --> CLI[CLI / Dashboard]
    CLI --> A[Agent loop]
    A <-->|tool calls| LLM[(LLM<br/>Groq · OpenRouter · Gemini · Ollama · OpenAI)]
    A --> T[Workspace tools<br/>read · search · edit · run]
    T --> G[Code graph<br/>SQLite]
    P[Parsers<br/>Python ast · tree-sitter JS/TS] --> G
    T -->|after every edit| I[Impact report]
    G --> I
    I --> A
    A -->|events| D[Live dashboard<br/>Server-Sent Events]
Module Responsibility
agent.py The reasoning loop: asks the model for the next action, executes it, feeds back the result
session.py One fixing session end to end: suspects, baseline, the ghost or a tournament, regression guard, proof, confidence, history
locate.py Where to look first: ranks the code most likely at fault
regression.py The regression guard: the whole suite before and after, and what broke
proof.py Red-green proof, and the step that writes a regression test when there is none
tournament.py Competing candidate fixes, cross-examined with each other's tests
haunt.py Risk ranking, the haunter, and the skeptic that confirms each bug
nightshift.py The unattended issue queue, pull requests and the morning report
ask.py Read-only answers and the call flow they describe
tools.py The agent's hands: sandboxed file access, edits, commands, graph queries, impact notes
graph.py The living graph: incremental SQLite index, callers, related tests, change impact
linker.py Links each call to the function it means, by each language's rules (imports, packages, crates, types)
parsers.py Turns Python (ast) and JS/TS (tree-sitter) into symbols, calls and imports
parsers_typed.py The same for Go, Rust and Java, plus the types the code states (cart *Cart, Cart::new())
server.py + web/ The dashboard: standard-library HTTP server and a dependency-free single-page UI
providers.py One OpenAI-compatible client for every model provider

Engineering notes

A few problems that shaped the design:

  • Free models are messy. They invent argument names (line_end for end_line), prefix tool names (repo_browser.read_file), emit malformed JSON, and announce "done" in plain text instead of calling finish. GhostPatch normalises all of these instead of failing, which is what lets small free models complete real multi-file fixes.

  • Models don't always use the tools they're given. Early runs showed a free model ignoring the graph tools entirely. Rather than prompting harder, the impact report is now pushed after every edit, so the graph's knowledge reaches the model regardless.

  • Graph identity has to survive edits. Re-indexing a file gives its symbols new database IDs, so the dashboard tracks highlights by fully qualified name, not by ID.

  • Anonymous test callbacks are invisible to call graphs. In JavaScript, test("…", () => {…}) has no function name, so tests would never appear as callers. The parser turns test and suite blocks into named symbols.

  • Matching calls by name is not enough. Every total() looked like it called every function named total, which made impact reports noisy. Calls are now resolved through imports, module aliases, decorators and self/this; only calls on unknown objects fall back to the name, and are labelled as guesses. In Go, Rust and Java the code states types outright, so a call on a typed parameter or variable is resolved exactly, and calls into the standard library get no link at all instead of a wrong one.

  • Each language hides its tests somewhere else. Go runs tests a package (folder) at a time, Rust keeps unit tests inside the source file, and Java needs a class to match its file name. That shaped the proof and the tournament: rival tests are swapped in at their own paths rather than renamed, and a Rust file whose test module changed counts as a test.

  • Macros hide calls. Rust's assert_eq!(total(&cart), 3) is how tests call code, but to a parser a macro's arguments are raw tokens. They are parsed again as an expression.

  • "The tests pass" can hide breakage. An agent often runs only its own new test. The regression guard runs the whole suite before and after, so a fix that breaks something elsewhere is caught and handed back, instead of shipped.

  • No build step, no heavy dependencies. The dashboard is plain HTML, CSS and JS with a hand-written force-directed graph layout, served by Python's standard library.

  • Big repositories expose what small ones hide. On Tokio, a call like x.new() on a value of unknown type was linked to every method named new: 609,000 links for 45,000 calls, 2% of them certain, and every edit relinked everything (7 s). Now a name shared by many functions gets no guess, a call links to one overload of a method, a library copied into two folders resolves to the nearest copy, and an edit relinks only the calls it can affect. Measured on real projects:

    Project Files First index Re-check, no change After one edit Where to look
    Tokio (Rust) 807 3.9 s 0.02 s 0.10 s 1.7 s
    Prometheus (Go) 990 7.7 s 0.04 s 0.17 s 4.0 s
    NestJS (TypeScript) 2,003 3.6 s 0.09 s 0.24 s 1.8 s
    Django (Python) 2,977 18.9 s 0.36 s 0.80 s 6.6 s
    Guava (Java) 3,275 21.5 s 0.06 s 1.18 s 9.5 s
  • An agent reads text anyone can write. Issues, comments and stack traces can carry instructions. So the ghost can't touch .git/, .ghostpatch/ or .env files, its commands run without API keys or GitHub tokens, secrets are redacted from everything GhostPatch publishes, "safe" commands can't reach outside the project, and only maintainers' comments reach it. See SECURITY.md.

Tech stack

Python · SQLite · tree-sitter · OpenAI-compatible APIs (Groq, OpenRouter, Gemini, Ollama, OpenAI) · Server-Sent Events · vanilla HTML/CSS/JS with SVG · pytest (313 tests, using a scripted fake model, a fake OpenAI-compatible server for end-to-end runs of the real CLI, and a fake GitHub CLI, so the suite needs no API key or network; Go, Rust and Java fixes are tested end to end with the real go, cargo and Maven) · GitHub Actions CI on Windows, macOS and Linux, including a run of the GhostPatch Action itself

Roadmap

  • Autonomous bug-fixing agent with sandboxed tools
  • Living code graph with automatic impact reports
  • JavaScript and TypeScript support
  • Live web dashboard
  • Free model providers
  • Undo, run history, approval modes, init and doctor
  • CI on Windows, macOS and Linux
  • GitHub integration: issue in, pull request out
  • Poltergeist mode, crash tracing, PR review, CI auto-fix, replay, test gaps, time-lapse, fallback, memory, confidence
  • Free-tier survival: provider fallback, readable quota errors, shortened history
  • Red-green proof, fix tournament, haunt mode, night shift, ask the graph
  • Label an issue, get a pull request (GitHub Action)
  • Go, Rust and Java
  • Security review and hardening; tested on large real-world repositories
  • Isolated git worktree for every run
  • Public benchmark results on SWE-bench

Running it

New to GhostPatch? The user guide walks through everything step by step: installing, getting free API keys and where they're stored, every screen and command, GitHub automation, and troubleshooting.

pipx install ghostpatch            # or: uv tool install ghostpatch, or pip install ghostpatch
ghostpatch init                    # pick a free provider, paste a key, add a backup key
ghostpatch doctor                  # check everything is ready
ghostpatch serve                   # the dashboard, at http://localhost:8765

Keys are saved once in your user settings, so they work in every project. Free keys: Groq, OpenRouter and Gemini. Setting up two means a run carries on when one provider's daily quota runs out.

Command What it does
ghostpatch serve Live dashboard: describe a bug, watch it get fixed, undo with a click
ghostpatch fix "…" Fix a bug from the terminal (@ISSUE.md reads the description from a file)
ghostpatch fix "…" --candidates 3 A fix tournament: three independent fixes compete and the best-proven one wins
ghostpatch fix <issue link> --pr Fix a GitHub issue and open a pull request when the fix is verified
ghostpatch haunt Hunt for unreported bugs in the riskiest functions (--list shows the ranking, --fix fixes them)
ghostpatch nightshift --approve safe Fix every issue labelled ghostpatch, haunt, and write a morning report
ghostpatch ask "…" Ask a question about the code; get an answer and the real call flow
ghostpatch pr Open a pull request for the latest run (or any run)
ghostpatch history / undo List past runs / roll one back
ghostpatch graph impact NAME Ask the code graph what a change would affect (also map, callers, tests, …)
ghostpatch review [PR] Blast-radius review of your changes or a pull request (--post comments on it)
ghostpatch trace crash.txt --fix Map a stack trace onto the code graph, then fix the crash
ghostpatch gaps Functions no test reaches (--write-tests N has the ghost cover them)
ghostpatch ci-fix --mode pr For CI: if the tests fail, fix them and open a pull request
ghostpatch share / timelapse / memory Export a run as HTML / replay the architecture / show what it remembers
ghostpatch bench --compare Run the benchmark with and without the code graph (--full runs the whole session, --localize needs no model)
ghostpatch workflow nightshift --write Add a GitHub Actions workflow (ci, issues or nightshift) to the repository
ghostpatch init / doctor Set up a provider and key / check the setup

Add --approve safe to let test runs go ahead without asking, and --poltergeist to have every fix attacked before you see it. In the dashboard, the Setup view shows the model, the fallback chain, the health checks and the repo memory, and adds GitHub workflows in one click.

On GitHub, for free, add one of these to .github/workflows/ (or run ghostpatch workflow): fix failing CI, label an issue, get a pull request or the nightly night shift.

More detail in CONTRIBUTING.md.

License

MIT

Release files for ghostpatch 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ghostpatch 0.9.0
File Size Uploaded
ghostpatch-0.9.0.tar.gz 279.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ghostpatch 0.9.0
File Interpreter ABI Platform
ghostpatch-0.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 480.8 kB

Release files / ghostpatch-0.9.0.tar.gz

Download URL ghostpatch-0.9.0.tar.gz
Size 279.8 kB
Tags Source
SHA-256 checksum
How to use checksums
e8cc99118f62ad876ab3f226dd8b83c61d4d5656ac08321adb48da9ab691c051
BLAKE2b-256 checksum
How to use checksums
f9c9e4bd4d2202bb03f0a7ce6bd9e49fb3b722f4a9d4c992ca3e4977186e22da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / ghostpatch-0.9.0-py3-none-any.whl

Download URL ghostpatch-0.9.0-py3-none-any.whl
Size 201.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6147d00be8a2cfc5a9469aecbf3366a0fc669e7f6a26f62917279a88bee0b38a
BLAKE2b-256 checksum
How to use checksums
b2f5742fdfa591a616c9ace730a31448221bcfe08681a55562744b76e4af8c6d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.9.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page