Bhoot reads a repository's git history and finds the people who left but whose code still runs, plus every file that died. A local Gemma writes each one a short remembrance. Git checks every word before it goes on the stone.
Ghosts found in Express
What it finds
Old repositories are full of both. Bhoot looks for two kinds.
People who haven't committed in a year or more, ranked by how many of their lines git blame still finds. TJ Holowaychuk last committed to Express in February 2014. 6,483 of his lines still run.
Every deleted file gets a tombstone with its birth, its death and the people at both ends. The cause of death is the commit that removed it. Express's original docs went in April 2012, 31 files in one commit called "removed old docs".
The checker
TJ's spirit persists, 3,527 silent years woven into 6,483 lines of code.
Most of Gemma's mistakes looked like this one, a true number pinned to the wrong noun. 32 commits became "32 contributors". 9 commits became "nine brief lives". A checker that only asks whether a number appears in the facts lets every one of them through, so this one reads what each number counts.
6,483 lines passes. 3,527 years fails.
"Born in 2012" has to be the year the file was born.
"Removed by Sebastian" has to match the commit that deleted it.
Months, quotes, file names, and any word that looks like a name.
A remembrance that fails gets one retry, and Gemma is told what it got wrong. If it fails twice, the stone carries plain dates. Every stone has a badge that says which happened.
How it works
log, blame, and every file a commit deleted. "TJ Holowaychuk", "Tj Holowaychuk" and "visionmedia" become one person.
Gemma 3 4B in Ollama, on the same machine. Warm, a little eerie, never mocking.
Every number, year, name and file is matched against git before the stone is carved.
Numbers
I labelled every first attempt by hand. Then I froze the checker in a commit and ran a repo it had never seen.
I tuned the checker on Express and request, so those numbers flatter it. Underscore is the honest test, and sixteen stones is small. The labels, every attempt Gemma made and the scoring script are in the repo, so you can disagree with me line by line.
Walk a graveyard
Why it runs locally
Who stayed late, who left, whose code got rewritten. Git reads it locally and Gemma runs in Ollama, so none of it goes to a model service.
The seed is fixed. When I changed the checker, I re-ran it on the exact attempts Gemma had already written and measured the difference.
Express has 387 people in its history. Writing to every one of them costs an evening of laptop fan, not a bill.
Run it
Node 22.18 or newer and Ollama. No dependencies and no build step.
# once ollama pull gemma3:4b git clone https://github.com/codeswithroh/bhoot && cd bhoot # any repo you have locally node src/cli.ts /path/to/your/repo --out graveyard open graveyard/index.html
Questions
No. Git reads the history, Gemma runs in Ollama on the same machine, and the output is one HTML file. There's no account and no API key.
Anyone who hasn't committed for at least a year before the repo's latest commit and still has at least 50 lines at HEAD. Both numbers are flags. Lockfiles, build output and binaries don't count.
People leave projects for good reasons. The prompt says "never mocking", and the page is built around Día de los Muertos, which is about remembering people by name with the details right. It reads more like a thank-you note.
It runs on an 8 GB laptop. A bigger Gemma is one environment variable away, and the checker stays the same. A better model makes fewer mistakes. The checker catches the ones it still makes.
It checks facts, not judgement. It knows whether "born in 2011" is true. It can't tell you whether "a quiet guardian" is fair.