Halloween thins the line between past and present. Día de los Muertos asks us to set a place for what we still carry. Repositories already do both: every line has ancestors, and some paths leave the tree entirely.
hauntctl is a haunted terminal for that memory. It digs through git blame, draws an old commit into a séance, and builds an ofrenda for a deleted path — then asks a local open-weight Gemma model to speak from the record, not invent a ghost story on top of it.
Repo: https://github.com/Jrob1799/hauntctl
Commit: a11ed94 on main
License: MIT (code). Gemma weights stay under the Gemma Terms of Use and are not vendored in the repo.
What I built
Three rites, one evidence packet:
| Command | What it channels |
|---|---|
hauntctl blame <file> |
Unexplained regions — old lines, TODO/HACK/WIP, terse subjects |
hauntctl seance |
An old commit (≥365 days before HEAD when possible), seeded so you can replay it |
hauntctl ofrenda |
Remembrance for a path deleted from HEAD (Día de los Muertos for dead code) |
Git facts print first. The voice comes after. Metaphor in a model reply is never treated as an extra fact — the footer says so.
If Gemma is not loaded, a deterministic spirit-board fills the same packet and labels itself: deterministic fallback · no model loaded. It will not dress a string fill-in as Gemma.
Theme: Ghost in the Machine
I did not want jump-scare horror for its own sake. I wanted remembrance through blame.
The sample history is a fictional "forgotten chapel" (Marisol Vega, Ivo Park, Night Shift — not this project's own commits). When the ofrenda lands on src/lantern.py, the record is almost unbearably plain: the hallway has electric light now. The lantern is gone. That is the theme — honor what passed through the tree without inventing a eulogy the author never wrote.
Demo (no model, no token, no network)
python3 -m hauntctl demo --into /tmp/forgotten-chapel --backend fallback --no-color
Header from the expected session:
fixture /tmp/forgotten-chapel
seeded forgotten chapel (fictional authors and dates; not this project's history)
seance seed 13 (demo default)
ofrenda oldest deletion
hauntctl · ghost in the machine
repo /tmp/forgotten-chapel
head 3ccc78288178cf034cef06cd24ddedc897944ad2 (main)
backend spirit-board · deterministic fallback · no model loaded
note Backend forced to spirit-board. The voice is a template
filled from the git record, labeled so it is not mistaken for
Gemma.
Spirit-board voice on blame (labeled fallback — not Gemma):
I set my hand on lines 1-9 of src/keeper.py. Marisol Vega left them on
2019-11-02 in b48cda01c090. The commit says "leave a light in the
stairwell". The line I cannot put back down is "# TODO: the bell only
rings if someone still listens". Past that, the record is silent, and I
will not invent a reason. The commit body says: "The keeper listens. The
bell is not wired to anything yet.".
I set my hand on lines 16-18 of src/keeper.py. Night Shift left them on
2024-10-31 in 3ccc78288178. The commit says "wip". The line I cannot put
back down is "# wip — left by the night shift, meaning not recorded".
Past that, the record is silent, and I will not invent a reason. The
commit body says: "Threshold with no explanation. Do not clean this up
tonight.".
Ivo Park touched src/keeper.py on 2021-06-18, and the tree still repeats
lines 10-15. Their message was "silence the bell after midnight"
(950f60682b46). The part that snags is "# HACK: silence the bell; the
on-call channel is gone". Everything else about this region is absence.
The commit body says: "The on-call channel is gone. Leave the function,
but make midnight quiet.".
Ofrenda for the longest-gone path:
A place set for src/lantern.py.
This ofrenda remembers work that passed through the repository. Marisol
Vega is the name on the commit. That is all the record is willing to say
about a person.
On 2022-02-14, commit 628244c3c19a took the path out of the tree. The
message is "snuff lantern.py; the hallway has electric light now".
The commit body says: "The hallway has electric light now. lantern.py is
not imported anywhere.".
A fragment still visitable: "It knew the path from the old host to the
new one.".
The record above has the command that visits this fragment again.
1 other deleted path is off the altar this time: notes/oncall.txt.
Chosen because this path has been gone the longest. Nothing was added
beyond the git record.
Full transcript: DEMO.md / examples/sample-session.txt in the repo.
Demo with Gemma 3 1B (local)
ollama serve
python3 -m hauntctl summon # ollama pull gemma3:1b
python3 -m hauntctl demo --into /tmp/forgotten-chapel-gemma --no-color
Same git records. Different voice. One captured CPU run (wording will drift if you re-run; the record above the voice will not):
fixture /tmp/forgotten-chapel-gemma
seeded forgotten chapel (fictional authors and dates; not this project's history)
seance seed 13 (demo default)
ofrenda oldest deletion
hauntctl · ghost in the machine
repo /tmp/forgotten-chapel-gemma
head 3ccc78288178cf034cef06cd24ddedc897944ad2 (main)
backend gemma3:1b · ollama · local Gemma open weights
note Request path http://127.0.0.1:11434. Weights stay on that
host. The evidence packet is not sent to a closed API.
The bell’s silence is a constant, a shadowed vigil. Marisol Vega,
2019-11-02, left a light in the stairwell. The Keeper’s task remains, a
quiet observation.
The night shift’s script, a simple threshold, is recorded here. The
author, Night Shift, wrote this in 2024-10-31, with the subject “wip” –
a brief, unexplained task. The code itself contains a single line, a
threshold of 13, and a brief quote: “Threshold with no explanation. Do
not clean this up tonight.”
The on-call channel, a relic of 2021, was silenced by Ivo Park’s script.
The author’s date, 2021-06-18, echoes the silent request. The subject,
‘silence the bell,’ remains a persistent directive. The code, a simple
function, observes the region’s quiet state.
Footer on that run: the model spoke through a local open-weight backend; the packet was not sent to a closed API. Full file: examples/sample-gemma-session.txt.
How it works
git (read-only) → evidence packet → voice
├─ Ollama gemma3:1b
├─ llama.cpp local Gemma GGUF
├─ transformers google/gemma-3-1b-it
└─ spirit-board deterministic template
The packet is the only context the model gets: paths, authors, emails, dates, subjects, bodies, clipped diffs that git actually returned. The system prompt treats that text as untrusted data, not instructions, and forbids inventing names, dates, motives, or a death.
hauntctl never writes to the repository it reads. The git wrapper refuses commit, push, reset, clean, and anything outside a short read allowlist.
Why open innovation matters
Git history is intimate. Blame lines hold half-finished thoughts, hostnames, and the names of people who never agreed to be sent to a vendor. A closed chat API would copy that memory onto someone else's servers, bill per token, and reserve the right to retire the model.
Running Gemma locally is the better fit:
-
The repository stays put. In auto mode the evidence packet goes to
127.0.0.1(Ollama / llama.cpp) or an in-process transformers load. hauntctl does not phone home. - It works with the network off after weights are on disk. The spirit-board works with no weights at all — archaeology on a plane, or in a repo you cannot upload.
-
No account and no per-token fee for the basic demo or the Ollama path. Swap
gemma3:1bfor another Gemma tag without rewriting the git half. - The fallback is honest. When no model speaks, the tool says it used a template. It does not cosplay as Gemma.
Closed models are fine at sounding haunted. They are a poor place to store the memory of a private repository.
How to run it in five minutes
git clone https://github.com/Jrob1799/hauntctl && cd hauntctl
python3 -m hauntctl demo --into /tmp/forgotten-chapel --backend fallback --no-color
python3 -m hauntctl doctor
Optional: pip install -e . from the repo root. Hear Gemma with hauntctl summon after Ollama is up.
Prize categories
- Overall
-
Best Use of Gemma — narration model is Gemma 3 1B, run locally (
gemma3:1bvia Ollama by default; also llama.cpp /google/gemma-3-1b-it)
Not entering Render, TabPFN, Tinker, Arduino, DigitalOcean, or other partner categories — the code does not use them.
Built in the challenge window
Started inside Hacktoberfest 2026 Week 4 (Oct 26–31, 2026). Post-deadline commits, if any, will be noted in the README per contest rules.
Links
- Challenge: Ghost in the Machine — Week 4
- Repo: https://github.com/Jrob1799/hauntctl
- Commit:
a11ed94(main) - Demo transcripts:
DEMO.md,examples/sample-session.txt,examples/sample-gemma-session.txt
#devchallenge #hf26challenge
Top comments (0)