a voice assistant that fits in one bash file
Jarvis listens to this machine, talks to it, and lives in the bottom-right corner of the screen as a small yellow fish. Everything he does goes through the table below. It is not a diagram: it is the table, frozen into a test fixture. Drive it.
A pair absent from the table is refused — loudly, in the log, without touching
the state record. That is the point: thinking:finish and
listening:speak are not states of the world, they are bugs
arriving.
Jarvis has no API. He has a shell, an allowlist, and the documentation of his
own machine in memory/knowledge/ — regenerated on every Omarchy
update. When a command is not obvious to him, he searches there before
inventing one.
hyprctl, launching applications.He can look at what you are looking at — only when you ask him to in the current exchange, never on his own initiative, never during a round or a dream. Every use leaves a line in his black box.
~/Jarvis is the file contract between you and him — three
folders, no database.
A real sequence, so it is numbered. Every long stage runs inside the invocation that entered it; a concurrent invocation touches only the state record and the process groups on disk. That is what makes interrupting safe.
The internal microphone opens. The wake daemon listens permanently: “Hey Jarvis” opens the ear, and 1.2 s of silence after speech closes it — a whole exchange without touching a key.
Whisper transcribes the recording. The language is pinned by his soul rather than detected: on a short phrase, automatic detection gets it wrong.
One Claude session, resumed across exchanges by an id of its own. The stream is read as it arrives: every tool call goes to the black box, and every finished sentence goes to the voice without waiting for the next one.
Piper synthesizes, offline. The voice starts on sentence one while the brain is still writing sentence two, and the bubble fills at reading speed.
Once the reply is out, a short window stays open: a rejoinder needs no wake word. Silence closes it quietly.
The state file says what the machine is doing. Beside it lives a one-line record saying which attempt is doing it — an epoch, an owner, an age. Every stage remembers the epoch it entered on; if that has moved, the stage goes quiet instead of finishing work nobody wants any more. That is what makes cancelling real.
The state file itself stays exactly one lowercase word: the wake daemon reads it, and so does the Touch Bar. It is a public contract.
record, stt, brain, voice,
play, dream: every external stage runs in its own
process group, whose id is written to disk.
Killing the group is what also reaches the subprocesses the brain spawned for itself. A bare pid never reaches them — and that is exactly why cancelling cancelled nothing, for a long time.
This grid is printed by the machine itself and frozen into
tests/fixtures/transitions.txt. The suite diffs the two: the
table cannot drift from its own documentation without a red line
appearing.
Nothing is hidden in a database. Everything Jarvis remembers is a file you can open, correct, or delete — and each one has a clear author.
knowledge/ documents the machine, one file per command group, regenerated automatically — nothing in there is written by hand. knowledge/inbox/ files the documents you handed him, and skills/ keeps how-tos for unusual tasks. His dreams write new ones.
A conversation is one session, with an id of its own — rounds, dreams and missions each open theirs, so a resume never lands on the wrong thread. After eight hours without a word, the conversation is summarized and closed: a morning amnesia that forgets nothing.
The fish is not an image: he is assembled from parts — four bodies, four eyes, four crests, four tails, eight colourways. Every gesture is checked against all 256 combinations before it is accepted. The sheets below are the repository’s own, at their real frame rates.
What he heard shows in pale ink, his reply types out underneath at reading speed, and a row of dots turns while he thinks. When he talks in his sleep, the bubble becomes a thought bubble. When you cancel, its tail breaks into two detached stubs and the sentence stops on an ellipsis — exactly where it had got to.
A pet that only exists when spoken to is a widget. Every ten to twenty minutes an idle fish takes an excursion along the bottom edge and comes back. He dozes off at night, wears headphones under do-not-disturb, tires when the battery runs low — and he sinks between two sheets instead of jumping: a change of state eats whole rows of pixels off the bottom, then rises again.
bin/jarvis the body: the organs, the verbs, what each stage does bin/jarvis-fsm.sh the table, the identity, the handles — sourced by the above bin/jarvis-sentences the brain's stream → speakable sentences + the black box bin/jarvis-wake.py the ears: the wake word, endpointing, the follow-up window bin/jarvis_wake_fsm.py their decision, pure — no audio, no model, testable bin/jarvis-index rebuilds knowledge/ by introspecting the machine plugin/ the shell plugin: the fish, his bubble, the prompt bar sprites/ the parts engine: parts, generate, check — 256 fish memory/ his soul at work: journal, trace, lessons, cards, prompts tests/ the offline suite — fake claude, fake piper, fake microphone systemd/ the pulse (60 s) and the inbox watch ~/Jarvis your exchange box: input, todo, output ~/.local/state/jarvis the log, the brain's health, the timestamps $XDG_RUNTIME_DIR/jarvis the state, the state record, the process groups
--quiet cuts the voice./install.sh lays the command symlink, builds the
plugin in a staging directory before swapping it in at once, installs and
enables the systemd units, then runs the self-check. Nothing is written file by
file into the plugins folder: the shell reloads on any change in there.
./tests/run runs the whole chain offline: a fake
brain, a fake voice, a fake microphone that really dies on a signal, and a full
sandbox for the state and the memory. No test touches your real memory, takes
the microphone, or makes a sound.