cd /news/ai-tools/tj-holowaychuk-left-express-in-2014-… · home › topics › ai-tools › article
[ARTICLE · art-160598] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=↑ positive

TJ Holowaychuk left Express in 2014. 6,483 of his lines still run. Gemma called his commits "3,527 silent years".

A developer built Bhoot, an open-source tool that reads a repository's git history locally and generates Día de los Muertos-style remembrances for departed contributors and deleted files, using a local Gemma 3 4B model via Ollama. Because the model is strong on tone but unreliable on facts, Bhoot programmatically verifies every name, number, date and file in each generated text against git before displaying it. Applied to Express, the tool merged TJ Holowaychuk's three commit identities via union-find and found 6,483 of his lines still running, with 3,527 commits and no activity since February 2014.

by read7 min views9 publishedOct 29, 2026

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine

In Hindi, bhoot means ghost. It also means the past. Bhoot kaal is the past tense.

Every old repository has both. There are people who stopped committing years ago but whose code still runs every time someone types npm start. And there are files that were born, argued over for a few hundred commits, and deleted, with the reason written in a commit message nobody will read again.

Bhoot reads a repository's git history on your own laptop and finds both:

git blame still finds at HEAD, and the oldest line of theirs that's still there. Then a local Gemma writes each of them a two-sentence remembrance, in the spirit of Día de los Muertos: warm, a little eerie, never mocking. And then code checks every name, number, date and file in those two sentences against git, because, as it turns out, Gemma is very good at tone and quietly bad at facts.

The theme asked for something spooky, or something that honours the past. A graveyard for your codebase is both. The ghost in the machine is literal: TJ Holowaychuk hasn't committed to Express since February 2014, and 6,483 of his lines still run.

Walk the graveyards: bhoot-graveyard.vercel.app

Light a candle for a ghost (it remembers, in your browser). Hover any badge to see what the checker verified, or what it threw out.

To raise your own:

ollama pull gemma3:4b
git clone https://github.com/codeswithroh/bhoot && cd bhoot
node src/cli.ts /path/to/your/repo --out graveyard
open graveyard/index.html

In Hindi, bhoot means ghost. It also means the past.

Bhoot reads your repository's git history, finds both, and writes them a remembrance that git can vouch for.

Walk a graveyard · Quick start · How it works · Numbers · FAQ

TJ Holowaychuk hasn't committed to Express since February 2014. 6,483 of his lines still run.

Every old repository has ghosts like that: people who moved on but whose code didn't. It also has graves: files that were born, argued over for a few hundred commits, and deleted, with the reason buried in a commit message. Bhoot finds both on your own laptop. A local Gemma writes each a short remembrance in the spirit of Día de los Muertos, and code checks every name, number, date and file in it against git before it's carved.

Everything Bhoot knows comes from four git commands: log (who committed when, and what they said), blame --line-porcelain at HEAD (whose lines are still here), log --diff-filter=D -M (which files died; -M so a renamed file doesn't count as a death), and show <commit>^:<path> (what a file said the day before it was deleted).

The first surprise was identity. In Express's history, TJ Holowaychuk is three people: TJ Holowaychuk, Tj Holowaychuk, and visionmedia, the name on his earliest commits. Counted separately, none of them is the top ghost. Bhoot merges identities with a small union-find: a commit joins its author's name and email, so any spellings that ever shared an email become one person. TJ came out as one ghost: 3,527 commits, silent since 2014-02-19.

Gemma 3 4B runs in Ollama on my 8 GB MacBook. For each stone it gets the facts as plain lines (name, first and last commit, counts, the oldest surviving line or the start of the deleted file) and one instruction: two sentences, Día de los Muertos in spirit, use only these facts.

It's good at this. "Born July 13, 2010, this guide of 1,366 lines, crafted by Tj Holowaychuk, served as a helpful starting point. It faded April 16, 2012, a quiet end after 168 commits." Every fact there is right.

Here is what Gemma wrote for TJ on the first run:

TJ's spirit persists, 3,527 silent years woven into 6,483 lines of code, still running from 2009's initial release.

Every number in it is real. 3,527 is his commit count. It just isn't years.

That turned out to be the most common way Gemma got things wrong, and not the way I expected. I expected invented numbers. What I got was real numbers attached to the wrong thing: 32 commits became "32 contributors", 9 commits became "nine brief lives", 145 commits became "145 silent years", and a man who was silent for three years was "quietly maintained for three years". A checker that asks "is this number in the facts?" passes every one of those.

So the checker doesn't just look for numbers. It reads what each number counts:

seanstrom. If a remembrance fails, Gemma gets one retry and is told exactly what was wrong ("it mentioned '3,527 silent years', which is not in the facts"). If it fails again, the stone carries plain dates instead. Every stone has a badge that says which happened.

The first full Express run rejected Gemma's first attempt on 28 of 42 stones, and 15 ended up carrying plain dates. By my labels, only 8 of those 42 first attempts were actually false. Most of the rejections were my checker's mistakes, not Gemma's:

Each of those is now a test, next to the cases Gemma got wrong.

I read every first attempt Gemma made and labelled it by hand: true, or saying something git contradicts. That includes a real number on the wrong thing. Then I scored the checker against my labels.

I built and tuned the checker on two repos, Express and request. Then I froze it in a commit (e2f3d9a) and ran a third repo it had never seen, underscore, once.

Gemma's first attempts that were false Caught by the checker True attempts wrongly rejected
express + request (checker tuned on these) 22 of 78 (28%) 22 / 22 0 / 56
underscore, held out 3 of 16 3 / 3 1 / 13

On the held-out repo, every stone that ended up carved was true: 15 of them in Gemma's words, 1 in plain dates. The one wrongly rejected attempt was the "Chambers'" apostrophe bug; Gemma's retry passed, so nothing was lost.

Sixteen stones is a small held-out set, and I'm the only labeller. The labels, every attempt Gemma made, and the scoring script are all in the repo (eval/), so anyone can disagree with me line by line.

request/request deleted sixteen proxy test files in one commit ("Merge all proxy tests into tests/test-proxy.js"). Sixteen near-identical tombstones made a boring graveyard, so files laid to rest by the same commit now share a family plot: one stone, with its siblings listed underneath.

Because a git history is personal. It's who stayed late, who left, who rewrote whose code, and what they said when they deleted something. For an open-source repo that's public anyway. For your company's repo, or your team's, it isn't. Bhoot never sends it anywhere: git reads it, Gemma runs in Ollama on the same laptop, and the graveyard is one HTML file. A hosted model would mean up your team's entire commit history to remember the people in it.

Because I could make it reproducible. With the weights on my machine I fix the seed, so the same repo gives the same first attempts every time. That's what made the evaluation honest. When I fixed the checker, I re-ran it on exactly the same 78 attempts Gemma had already written and measured the difference, instead of hoping a new sample came out similar.

Because it costs nothing to remember everyone. Express has 387 people in its history. Writing a remembrance for each of them, with retries, is a lot of calls. Locally it's an evening of a laptop fan, with no budget to think about.

Because the model is swappable. BHOOT_MODEL=gemma3:12b uses a bigger Gemma, and the checker stays the same either way. The checker matters more than the model: a better model will make fewer "3,527 silent years", and the checker catches the ones it still makes.

I ran it on my own team's repo. Everyone was surprised first, and then it turned into a lot of laughing and teasing. Some of the team printed an image of the graveyard and put it up on their walls. Showing it to the whole team was a lot of fun.

Día de los Muertos isn't about mourning. It's about remembering people by name, with the details right.

That's what made the checker matter more to me than the prose. A stone that says "Born in 2012" when the file was born in 2011 isn't a remembrance. It's a mistake with a nice font. Getting the small facts right, the year, the line count, the name of the person who wrote it, is the part that honours someone.

Some of what the graveyards remember:

test/arrays.js, var list = [1, [2], [3, [[[4]]]]];.

── more in #ai-tools 4 stories · sorted by recency
── more on @bhoot 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/tj-holowaychuk-left-…] indexed:0 read:7min 2026-10-29 · —