DEV Community

Cover image for git séance: I asked a local Gemma to channel the ghosts of a dead AngularJS codebase
Triin Kask
Triin Kask

Posted on AI-assisted

git séance: I asked a local Gemma to channel the ghosts of a dead AngularJS codebase

Hacktoberfest Open-Source AI Challenge: Ghost in the Machine Submission 👻🌼

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

What I Built

Every legacy codebase has a line like this:

$timeout(poll, 3000); // do not lower!
Enter fullscreen mode Exit fullscreen mode

Nobody remembers who wrote it. Nobody dares to change it. The person who knew why left the company years ago.

But the reason usually isn't gone. It's buried in commit messages that nobody reads. The code is haunted, and the ghost has been trying to tell us something the whole time.

git séance is a small command-line tool that holds a séance for your source code. You point it at a line, and a local Gemma model becomes the ghost of that line. It tells you, in the first person, how it was born, who changed it, and what it has survived, citing the real commits as it goes.

seance why src/app.js:214   # the ghost of one line tells its story
seance haunt                # a graveyard of forgotten files and ancient TODOs, with epitaphs
Enter fullscreen mode Exit fullscreen mode

I work with AngularJS code every day, so the theme's "forgotten technology" angle felt personal. AngularJS reached end-of-life at the end of 2021, yet a lot of it still runs in production. Those codebases are full of ghosts.

Demo

https://github.com/TriinK/git-seance/blob/master/docs/demo.gif

Code

🕯 git-seance

Ask the ghosts in your git history. Point git-seance at any line of legacy code and a local Gemma model tells you its story, in the first person, citing the real commits that shaped it.

seance haunt and seance why running on the archived angular.js repo

$ git clone https://github.com/angular/angular.js && cd angular.js   # archived, 9,000+ commits
$ seance why src/ng/httpBackend.js:59
   55 | }
   56 |
   57 | function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
   58 |   // TODO(vojta): fix the signature
>> 59 |   return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
   60 |     url = url || $browser.url();
   61 | 
   62 |     if (lowercase(method) === 'jsonp') {
   63 |       var callbackPath = callbacks.createCallback(url);

5 commit(s) have touched this line.

🕯  Lighting the candles... summoning gemma4:e4b

I am the silent witness to this function's birth. I remember the beginning, when I was simpler, carrying only the basic parameters [5ad0c7d0e].

The journey began with the
…

How I Built It

The rule I set myself: the AI tells the story, but git provides the facts.

1. Evidence, gathered with plain git. git log -L214,214:src/app.js is one of git's most underrated features. It returns every commit that ever changed that exact line, with full commit messages and diffs. For haunt, a single pass over git log --name-only finds files untouched for years, and git grep plus git blame dates every TODO, FIXME and HACK.

2. The medium: Gemma 4 E4B, running locally through Ollama. The evidence and the surrounding code go to gemma4:e4b with a system prompt that sets strict rules. Speak as the ghost. Use only the history provided. Cite a commit hash for every event. If the reason was never written down, say it was lost to time instead of guessing. Finish with practical "advice from beyond". The answer streams to the terminal token by token, which makes the séance feel properly slow and eerie.

I chose E4B because it runs comfortably on a normal developer laptop, and its 128K context window means even a heavily rewritten line's whole history fits in one prompt.
On my laptop (Apple M4 Pro, 48 GB RAM) a story takes about 25 seconds from the first candle to the last word. seance haunt on the whole 9,000-commit AngularJS history takes about a minute, most of it spent in git blame dating old TODOs before the model even wakes up.

3. The skeptic. Ghosts lie, and so do language models. After the story arrives, git séance extracts every hash-like token and checks it against the real commit list. If the model cites a commit that doesn't exist, you get a warning instead of a confident fabrication.
In my runs on the AngularJS repo it hasn't caught Gemma inventing a hash yet. Every citation matched a real commit. The check stays anyway, because the one time it matters is the time you didn't look.

The project is about 400 lines of Python, its only dependency is the ollama package, and the tests use a throwaway repo with backdated commits and a fake model, so they run anywhere.

Why Does Open Innovation Matter?

For this tool, an open-weight model isn't a nice-to-have. It's the only way the idea works where it's needed most.

The most haunted code is the code you're not allowed to send anywhere. Legacy systems live inside companies, government agencies and client projects, often under contracts that forbid sending source code to a third-party API. A model that runs on my own machine means the code, the commit history and the names of everyone who ever touched it never leave the laptop.

It works with the lights off. Once the model is downloaded, git séance works fully offline: on a train, on an air-gapped network, or long after some hosted AI product has been retired. That felt right for a tool about things that outlive their creators.

It's inspectable all the way down. The evidence-gathering is plain git you can run yourself (--no-ai shows exactly what the model sees), the prompts are in the repo, and the model weights are public. Nothing about the séance is a black box, which matters when you're deciding whether to trust a story about why production code exists.

The Spirit of It

The moment it landed for me was the httpBackend.js run above. The TODO on line 58 says fix the signature. Vojta Jina wrote it in August 2011, in the commit that created the file. Over the next five years the signature on line 59 gained four more parameters, from four separate feature commits, and nobody ever fixed it. The TODO is still there, 15 years later, in an archived repository.

The ghost's "advice from beyond" was to fold the optional parameters into a single configuration object. That is exactly what the TODO was asking for in 2011. The model had no idea the TODO existed; it only saw the history of line 59. The code had been trying to say the same thing the whole time.

The graveyard was quieter but just as telling. The oldest ghosts in AngularJS are three image files, untouched for 16 years, whose last commit message reads inline all images into css. The commit that made them obsolete is the last one that ever touched them. The caretaker's epitaph:

[1] Here lies images/css/arrow_left.gif, untouched for 16 years, forgotten after the instruction to 'inline all images into css'.
Enter fullscreen mode Exit fullscreen mode

Prize Categories

  • Best Use of Gemma: Gemma 4 E4B runs locally through Ollama and is the core of the tool. It turns raw git history into a grounded first-person narrative and writes the graveyard's epitaphs.

Top comments (0)