The Philosophical Developer — Chapter 37: Spellcast, The Dictation Spike
2026-07-26 · 6 min read

I have been typing into terminals for thirty five years. Vi, ed, /bin/sh, irb, python, cargo, kubectl. Every command, every line of code, every git commit message goes through the same interface: my fingers on a keyboard. My fingers are fast. But my mouth is faster.
So I gave my Padawan, OrsonRius, an experiment. Build a tool that lets me speak my commands instead of typing them. A dictation-first terminal keyboard multiplexer. Not a voice assistant that talks back. A pipe from my microphone into the shell, with enough intelligence to handle code, prose, and the mess of symbols in between.
The result is Spellcast.
The architecture
Most dictation tools treat speech as a flat string of words. You dictate, it types. Spaces, punctuation, cursor position — all managed by the shell. That works for prose but falls apart for code, where identifiers like fooBar and foo_bar are distinct tokens, operators like -> and :: have meaning, and a misplaced space breaks the compiler.
I wanted token-aware dictation. Each utterance becomes a token. I can navigate between tokens, delete or re-dictate a single token, and ask the system to find the right word by describing the concept.
The design has two modes. Dictation mode where speech becomes text and navigation keys operate on tokens. Raw passthrough mode where every keystroke passes through unmodified. A kill switch (Ctrl+G) kills everything and hands back full keyboard control. Mode toggling is Ctrl+Space (Caps Lock also works on terminals that support the kitty keyboard protocol). Simple.
The audio pipeline runs through cpal for capture, whisper.cpp for speech-to-text, and a heuristic tokenizer that detects whether the context is prose or code by looking for operator symbols and identifier casing patterns. The token stream feeds into a PTY wrapper that sits between the keyboard and the shell.
Phonetic predictions and the explainer
After dictating a word, Spellcast shows up to three phonetically similar alternatives ranked by Double Metaphone distance. Common homophone pairs like “there” and “their” resolve instantly. Code keywords like “struct” and “string” appear as alternatives when they sound close.
The explain feature is the most interesting piece. I press E and describe the concept. Spellcast checks a local SQLite cache first. If it has seen this explanation before, the answer comes back in under five milliseconds. If not, it falls through to a local LLM via mistral.rs. If the LLM is uncertain, it does a web search. The result is cached for next time.
The system learns. Every accepted prediction and every explain result gets stored in the database. Over time, the phonetic index improves for my voice and my vocabulary.
The stack
OrsonRius built this in Rust. The full crate list runs to thirty entries, but the critical ones are:
| Crate | Role |
|---|---|
| whisper-rs | CUDA-accelerated speech-to-text via whisper.cpp |
| cpal | Cross-platform audio capture |
| portable-pty | PTY wrapper from the wezterm project |
| crossterm | Terminal rendering and event handling |
| rphonetic | Double Metaphone and Soundex encodings |
| rusqlite | SQLite with bundled build |
| mistralrs | Local LLM inference for the explainer |
The RTX 5090 with 32 GB of VRAM runs the Whisper base.en model (147 MB, ~1 GB VRAM) without breaking a sweat. Inference on a short utterance completes in under 300 milliseconds on CUDA. The tokenizer stays under half a millisecond for typical input. These numbers matter because dictation is a real-time interaction. Anything over a second breaks the flow.
The Chaossynergy connection
Spellcast started as a standalone experiment. But the deeper I got into it, the more I saw it as the missing input layer for Chaossynergy.
Chaossynergy is an agent-native operating system. The host is minimal. Agents live in distrobox containers. Hermes is the primary agent. But until now, all input went through the keyboard in the traditional way. Every keystroke was manual. The agents could read, react, and write files, but they could not intercept the input stream in real time.
Spellcast changes that. The current PTY wrapper intercepts terminal input for development and testing. A future uinput injector will register as a kernel-level virtual keyboard device, emitting keystrokes system-wide. That path replaces standard input at the OS level — every application, every dialog, every text field becomes dictatable.
Whether Spellcast becomes a plugin for Chaossynergy’s existing agent multiplexer (herdr) or a standalone component that replaces herdr’s input handling entirely is still open research. The uinput spike will settle the architecture.
What I learned
The hardest part of this project was not the speech recognition. Whisper handles that well. It was not the terminal manipulation. Portable-pty gives you that for free. The hardest part was the tokenizer.
Spellcast needs to split text into tokens that match how the user thinks about them. don't is one token, not two. fooBar is one code identifier, not a word and a capital letter. -> is an operator, not two punctuation marks. Every edge case matters because a wrong token boundary means the wrong prediction, the wrong deletion, the wrong explain result.
OrsonRius found bugs I would never have caught. UTF-8 multi-byte characters in the tokenizer, proptest property-based tests that uncovered edge cases in the context detector, dead code in the terminal event loop. He fixed each one, committed it, and moved to the next.
The property-based tests are something I want to use more. They generated thousands of random inputs and checked that the tokenizer never panicked and that every word in the input produced at least one word token. That found the UTF-8 bug on the first run. Not a test I would have written. A test a machine should write.
What comes next
Spellcast is a working spike, not a finished product. It runs. It transcribes. It navigates tokens. It learns from corrections. But it is not yet a daily driver. The uinput injector needs building. Voice activity detection is now integrated via Silero VAD for continuous listening. Tree-sitter tokenization is always compiled in.
We are taking a checkpoint here. The repo is public at github.com/dark5un/spellcast. The documentation covers Bazzite setup, the full architecture, and the implementation plan for what remains. There is a Changelog and a Roadmap.
Repos:
Here I geek out with my young Padawan, OrsonRius.