horizon-browser
Control, inspect, or audit Horizon browser panels through public browser_* MCP tools.
Check that a microphone is usable for Horizon dictation and report which layer is at fault — no signal, bad level, or a genuine model/accent limit. Use when dictation produces wrong, unrelated, or empty text.
$ npx -y skills add peters/horizon --skill horizon-speech --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/horizon-speechContext preview
The summary Claude sees to decide when to auto-load this skill.
Check that a microphone is usable for Horizon dictation and report which layer is at fault — no signal, bad level, or a genuine model/accent limit. Use when dictation produces wrong, unrelated, or empty text.
name: horizon-speech description: Check that a microphone is usable for Horizon dictation and report which layer is at fault — no signal, bad level, or a genuine model/accent limit. Use when dictation produces wrong, unrelated, or empty text.
Run this when a user says dictation "types the wrong thing", produces text they never said, or produces nothing.
Whisper-family models **hallucinate fluent, plausible text when fed silence**. Given a digitally silent recording, NB-Whisper will confidently emit something like `Esther Smith, forfatter` — grammatical, idiomatic, and completely unrelated to the user. The output looks like a model, accent, or dialect problem. It is almost always a capture problem.
So: **measure the capture level before touching models, languages, or config.** Never conclude "the model is bad at your accent" until step 3 shows real signal.
A user cannot tell these apart from the transcript alone. That is the whole reason this skill exists.
Horizon resolves its config by checking, in order, `$HOME/.horizon/`, then `$XDG_CONFIG_HOME/horizon/`, then a relative `horizon.yaml`/`horizon.yml` in the working directory. Resolution keys off `HOME` on every platform — **Windows included**; `USERPROFILE` is never consulted, so a native Windows launch without `HOME` set falls back to the relative path. Confirm which file is actually live before trusting it, or you may inspect a config Horizon never loaded.
Read `features.speech` from that file:
normalized identity key.
**If the configured name matches no present device, Horizon falls back to the system default and only logs a warning.** The user never sees it. A config naming a microphone that is no longer plugged in therefore looks like it works. Check the name against the live device list before anything else.
List input devices:
`ffmpeg -f avfoundation -list_devices true -i ""`
`Get-PnpDevice -Class AudioEndpoint` in PowerShell
Also confirm the device is not a **Bluetooth headset in HSP/HFP mode**. That profile is narrowband and heavily compressed; it degrades accuracy badly. Prefer a wired or USB microphone, or force the A2DP-era high-quality input if the stack offers one.
Ask the user to speak normally for about six seconds. Record mono at 16 kHz — that is what the models consume.
**Record from the device step 1 matched, never the system default.** When `input_device` names a non-default microphone, sampling the default measures a different device than Horizon uses and can yield the exact opposite diagnosis — a silent default while the real microphone works, or the reverse.
where `<device>` is a name from `arecord -L`
Only fall back to the default device when `input_device` is empty, since that is what Horizon itself then uses.
Tell the user **when recording starts**. If you launch the recorder as a background task, add a visible countdown first — otherwise the window elapses while they are still reading your message, and you will measure an empty room and misdiagnose it as a dead microphone.
`level.py` sits next to this `SKILL.md`. Invoke it by its full path — the working directory is normally the user's workspace, not the installed skill directory, so a bare `level.py` will not be found:
python3 <dir containing this SKILL.md>/level.py sample.wav
On Windows use the standard launcher: `py -3 ...\level.py sample.wav`.
It needs only the Python 3 standard library, and reports peak, RMS and clipped samples, each as a percentage of full scale so the numbers mean the same thing at every sample width:
| Reading | Meaning | Fix | | --- | --- | --- | | `peak` below 0.6% | No signal — capture muted, or no device | Unmute capture; confirm the right device is selected and present | | clipped samples > 20 | Too hot — distortion | Lower gain; turn microphone boost **off** first | | `rms` above 25% | Hotter than necessary | Reduce gain slightly | | `rms` 5–25% | Ideal for ASR | Nothing to do | | `rms` 2–5% | Usable | A little more gain would help | | `rms` below 2% | Too quiet | Raise gain, or move closer |
Run the profile's model against `sample.wav` and compare with what the user actually said. Horizon's models are transcribe.cpp GGUFs; if a `transcribe-cli` build is available:
transcribe-cli -m <model>.gguf -l <lang> sample.wav
Otherwise have the user dictate the same sentence in Horizon and compare.
Combine steps 3 and 4 — this is the part the user cannot do alone:
hallucinated. Fix the microphone; the model is fine.
room noise only, or the user did not speak inside the window.
accuracy will improve, especially on consonants.
technical terms** → this is the model's genuine limit, not the microphone. Suggest an initial prompt to bias vocabulary, or a profile whose model suits the language be
GPU-accelerated terminal board that puts all your sessions on an infinite canvas
Repo: peters/horizon
Control, inspect, or audit Horizon browser panels through public browser_* MCP tools.
Manage Horizon native VNC Device panels and drive isolated local desktops for simulators and native application tests through device_panel and the…
Notify the Horizon workspace user about completed work, findings, or needed decisions. Only works when HORIZON env var is set.