Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine Submission 👻
This is a submission for the Hacktoberfest Open-Source AI Challenge: Week 4.
What I Built
haunted-shell is a tiny ghost that possesses my terminal. A shell hook watches each command I run, and a small open-weight model on my own machine whispers back one short, eerie comment. It never touches the command itself - it only watches and whispers.
Real ghost output from the build's test runs. Each line below is a sample command string fed to the tool as text input (never executed), with an exit code passed alongside (2-core VM, model on CPU, no API key, no account):
$ git push origin main
👻 your code is spreading like a plague... across the night
$ rm -rf build/
👻 ridding yourself of the unwanted... but beware, there's no going back now
$ grep -r TODO .
👻 letting the shadows whisper through the files
$ sudo systemctl restart nginx (sample string; exit code 1 passed as input)
👻 nginx's head is stuck in a wall, you need a wrench.
That last one is technically nonsense, but a 1.5B ghost doing its best is pretty much the Halloween brief.
This is the Halloween sibling of lazycommit, my Week 3 project. Same machine, same idea of a small local model doing one fun job, new costume.
The ghost needed a bigger brain
The first version used the exact same model as lazycommit: Qwen2.5-Coder 0.5B Instruct, about 400 MB. For commit messages it worked well in the Week 3 tests, where it picked the right Conventional Commit type on all three real commits. In the ghost test runs it just repeated the input back like a court stenographer:
$ git push origin main
👻 A mortal ran: `git push origin main`.
So the ghost moved into Qwen2.5 1.5B Instruct, about 1 GB, still tiny, still running on plain CPU, and the whispers started sounding like whispers.
The second fix reused a pattern from the Week 3 project: a small model follows a few concrete examples much better than a paragraph of instructions. My first prompt was rules only, and the model kept retelling the command instead of commenting on it. With four short example whispers in the prompt (one about ls, one about git commit, one about ping, one about mv), it imitated the format right away.
How it works
The pipeline is small on purpose:
- A
PROMPT_COMMANDhook captures the last command and its exit code after each command finishes. - It calls
haunt whisper -- "$last_cmd" "$exit_code"in the background, detached. Your typing never waits for inference, and if anything fails the CLI exits silently with code 0. A lock file keeps it to one ghost at a time - if you type faster than it thinks, extra whispers are dropped instead of stacking up. A ghost must never break a shell. - Boring commands (
ls,cd,pwd, ...) are skipped, and--every Nthrottles it to every Nth command so it stays funny instead of noisy. - Commands that look like they contain credentials (passwords, tokens, keys) are skipped entirely - the ghost never sees them.
- The command is trimmed to 120 characters and sent, as data, with the ghost persona plus the four examples. Nothing is ever evaluated or executed - the ghost only reads.
- The reply is squashed to one line and printed dim and italic with a 👻 prefix.
The CLI is plain Python with zero dependencies for the default backend. It talks to a local server that speaks the OpenAI chat API. Verified in testing: the HTTP backend against a mock server, and the in-process llama.cpp backend that produced every whisper quoted above. Ollama and LM Studio speak the same API and are expected to work, but are not verified yet. The endpoint is loopback-only by default and the client refuses to follow HTTP redirects, so a misconfigured local server cannot bounce your commands somewhere remote. A deliberate HAUNT_ALLOW_REMOTE=1 override exists for people who run the model on another machine of their own. There is also an optional in-process backend using llama-cpp-python if you do not want to run a server at all.
On a 2-core VM with the model on CPU, one whisper took about 2 to 5 seconds. That is one measured result on that VM, not a benchmark. The ghost runs detached, so you never sit waiting for a reply - but it does spend those few CPU-seconds per whisper in the background, so on a weak machine you will want --every N rather than a comment on everything.
The project has 17 pytest tests covering the prompt builder, the throttling, the fail-silent behavior, the secret filter, the loopback guard, redirect refusal, and the HTTP backend against a mock server. All pass. I also tested the hook in an isolated shell: it whispers on real commands, stays quiet for ls, preserves the exit code of a failed command, and disappears cleanly when you remove the line from your shell config.
Why open innovation matters here
Shell commands are some of the most sensitive text on a machine. They carry folder names, project names, hostnames, sometimes worse. A ghost that sends your command history to a cloud API is not a ghost, it is spyware with a Halloween costume.
Because the model is open-weight and runs on my own machine:
- Private: with the default configuration, whispers are generated on loopback only and the tool never writes command text to disk. Your shell's own history behaves exactly as it always has - that part is unchanged by this tool. And to be clear about the limit: local processing is not secret-proof, which is why likely-credential commands are filtered out before the model ever sees them.
- Offline after setup: installing the package and downloading the model need the internet, once. After that, whispers need no network at all. The tool itself needs no API key and no account.
- Free: no per-token bill for a toy, no subscription.
- Swappable: when the 0.5B coder model turned out to be a bad ghost in testing, I swapped in a 1.5B instruct model by changing one string. No vendor migration, no new SDK, no new pricing tier. The same OpenAI-compatible interface meant the whole tool kept working.
That swap is the open-ecosystem advantage in miniature: not just free as in money, but cheap to change your mind.
Code
Source code on GitHub: https://github.com/MRNOWSHIRWAN/haunted-shell
The application code is MIT licensed. The model weights keep their own Apache-2.0 license.
Setup, if you have Ollama:
pip install git+https://github.com/MRNOWSHIRWAN/haunted-shell.git
ollama pull qwen2.5:1.5b-instruct
haunt install-hook >> ~/.bashrc
exec $SHELL
Uninstall is deleting that one line from your ~/.bashrc.
Happy Halloween. Check your shell before you type 👻

Top comments (0)