| title | CLI Reference |
|---|---|
| description | Hand-written, example-first guide to every yazses command and subcommand, grouped by the panels the CLI itself uses. |
All commands are available as yazses <command> once installed globally
(uv tool install / pipx install), or as uv run yazses <command> from the
repo.
Looking for the exhaustive, option-by-option list? See the auto-generated Command Index — every command, argument and flag, generated straight from the app. This page is the hand-written companion: it groups the commands the way
yazses --helpdoes, and gives each a synopsis, its key options, and at least one worked example. For the full config surface see the Configuration Reference; for the full feature catalogue see the Feature Reference.
Getting help. Every command and subcommand accepts both -h and --help;
each shows its options plus a worked Examples block. yazses --help lists all
commands grouped into six panels — Updates & maintenance, Daemon,
Setup & calibration, Dictation & correction, Remote, and
Learning & tuning — and bare yazses shows the same help. yazses --version /
-V prints the version.
Tab completion. Run yazses --install-completion once to enable <Tab>
completion of commands and options in your shell (yazses --show-completion
prints the script to inspect or customise).
The sections below follow the same six panels.
| Command | Description |
|---|---|
yazses about |
Print a branded banner with the author, version, project links, and where to report a bug or request a feature. |
yazses update |
Check for a newer version and offer to install it. |
Shows a branded banner: author, installed version, project links, and where to report issues or request features (issues: https://github.com/MSKazemi/yazses/issues; author: Mohsen Seyedkazemi Ardebili mohsen.seyedkazemi@gmail.com). Read-only.
yazses about # author, version, links, and where to report a bug or request a featureThe banner draws the YazSes mark — a "Y" over a listening sound-wave, the same
logo as the docs site, the Snap listing, and the tray icon — with the brand
gradient swept across it. It degrades automatically and never needs configuring:
truecolor to 256-colour to no colour, and Unicode blocks to plain ASCII on a
terminal whose codepage cannot encode them. Piped, redirected, or with
NO_COLOR set, you get a single plain
YazSes <version> — <tagline> line instead, so captured output stays clean.
yazses quickstart shows the same banner, and is the only command that animates
it: the sound-wave ripples briefly and settles. That never happens off a TTY,
under NO_COLOR, or in CI.
Detects how YazSes was installed and checks the matching source — the tracked snap channel for snap installs, PyPI for pip / pipx / uv-tool — then upgrades only when the available version is strictly newer (never a downgrade).
Options: --check (report only, don't install) · --yes / -y (install
without prompting).
yazses update # check for a newer version and offer to install it
yazses update --check # only report what's available (don't install)
yazses update --yes # install without askingAfter a successful update, restart the daemon to load it:
systemctl --user restart yazses (or yazses stop && yazses start).
| Command | Description |
|---|---|
yazses start |
Start the daemon; restarts cleanly if one is already running (never a duplicate). |
yazses restart |
Stop all daemons (including stray/detached ones) and start exactly one. |
yazses stop |
Stop the running daemon. |
yazses status |
Show state, hotkey, model, injection backend, uptime, decode latency (p50/p95) and how many recent bursts produced text, over IPC. |
yazses tray |
Show the top-bar tray icon + click-menu (pick/pin mic, re-calibrate, start/stop). |
yazses features |
See capabilities and turn them on/off — no config-file editing. |
yazses settings |
The same switchboard as a window — every capability as a checkbox. See the Settings window. |
yazses-daemon |
Run the daemon in the foreground (logs to console) — useful for debugging. |
Loads the speech model once (first run 10–30 s) and listens for the hotkey.
Routes through systemd when a yazses.service user unit is installed (so it's
supervised and self-heals), else spawns detached. Verifies the daemon actually
came up: reports when it's ready, notes when it's still loading, or reports a
startup crash with the reason and exits non-zero. If one is already running it
restarts it (killing any stray duplicates) rather than spawning a second — so
you never double-type. Warns if a runtime prerequisite is missing (run
yazses setup) or if an input-group re-login is still pending.
yazses start # start dictating — hold the hotkey, speak, releaseStop all daemons (including stray/detached ones) and start exactly one, then
verify readiness (same as start). Use this if dictation is being typed twice (a
sign of duplicate daemons). Same prerequisite warnings as start.
yazses restart # stop every daemon and start exactly oneStop the running daemon (SIGTERM). Dictation stays off until you yazses start
again; to pick up a config or version change instead, use yazses restart. Exits
non-zero (with "nothing to stop") when none is running.
yazses stop # dictation off until you start againShow state, hotkey, model, injection backend, and uptime over IPC. When not
running, points you at yazses start (and yazses quickstart for new users);
while the model is still loading it says so rather than looking broken. Pass --json
for machine-readable JSON output for status bars or scripts.
yazses status # is it running? show state, model, and hotkey
yazses status --json # output state, pid, model, and ready as JSONUptime counts time the machine spent asleep. It answers "how long has this process existed", which is how you notice a daemon still running the build it started with — a daemon keeps that build until it is restarted, so a long uptime next to a freshly upgraded CLI means the two disagree. Measuring it with a clock that stops during suspend made it read low on exactly the machines most likely to be stale: a laptop that sleeps eight hours a night under-reported by eight hours a day.
It also reports decode latency on your machine, per model:
latency: small.en p50 740 ms / p95 1210 ms (n=143)
p50 and p95, not an average. Decode time is right-skewed — most utterances are fast and a minority are slow — and the slow ones are the whole experience, because that is the moment you are sitting there with the key already released. A mean averages that tail away and looks healthy the entire time; p95 is the number that predicts "this feels laggy".
Reported per model, which is what makes it actionable: it turns "should I run
tiny.en or base.en on this machine" from a guess into a measurement of your
hardware and your voice. The benchmarks page gives figures for one
machine; this gives you yours.
The sample count is always printed, and below 20 samples the p95 is withheld rather than shown — a p95 over six utterances is not a p95, and printing one invites reading it as one. The window is bounded (the last 200 utterances per model) so the numbers still move after you change model, which is exactly when you look at them.
And it reports how often dictation actually produced text:
typed: 18 of 20 recent dictation bursts (90%)
commands: 4 recent command burst(s), 1 unrecognised
A fast decode that types nothing is not usable at all, so this is the more basic of the
two numbers. A burst counts as typed only if text reached the window: one that was
discarded (silence, an empty transcription, no text target) or that raised does not,
which means the figure cannot flatter itself by counting failures as successes.
Bursts held on the command key are counted separately, and the second line only
appears on a machine that uses one. Command mode never types literal text — an
unrecognised phrase is ignored by design — so averaging the two produces a number that
describes neither: a command that matched would flatter the dictation rate, and one
that did not would read as a dictation failure. On the machine this split was written
for, four of six recent bursts were commands and the combined line said 0% while
dictation was working, directly above a microphone warning.
It is a gauge, not a warning — it shows on a healthy run too, because a number that only appears when something is wrong gives you no baseline to compare against, and you cannot tell 70% from 100% at the moment it matters. Like the latency window it is bounded and recent, so a change that started this morning is visible rather than averaged into months of history; and it stays silent below five bursts, because "0% of 1" would be believed.
This needs nothing turned on: the samples live in memory in the running daemon, are never written to disk, and do not involve any audio or transcript text. They reset when the daemon restarts.
Speak, review, then commit. With staged mode on, a hold-to-talk burst lands in a buffer instead of typing into the focused app; nothing reaches the application until you commit it.
For prose, typing straight through is the whole point of hold-to-talk. For code and
terminal input it is the wrong default: a mis-transcribed token is not a typo you
skim past, it is a command you did not mean to run. scratch that cannot help —
by the time you say it, the wrong text is already in your shell.
yazses features enable staged # off by default
yazses staged status # what is waiting, and how much
yazses staged commit # type it into the focused app
yazses staged undo # drop the last burst ("scratch that" does this too)
yazses staged discard # drop everything pendingCommit is a deliberate action, not a spoken word. A spoken commit inside a buffer
whose purpose is catching mis-recognition can be triggered by the very
mis-recognition it exists to catch — prose misheard as "commit" types early, which is
the accident staged mode was turned on to prevent. A missed commit is visible and
costs one more keystroke; a premature one has already run. Turn the spoken phrases on
with [staged] spoken_commit = true if you want them; they are matched as whole
utterances, so "git commit -m fix" is staged rather than obeyed.
While something is pending, "scratch that" edits the buffer, not the document — the buffer is what you are looking at.
Show a microphone icon in the top bar with a click-menu — a no-terminal way to manage the mic and daemon. The menu lets you pick/pin the input microphone from a live device list, re-calibrate the mic level, and restart/stop the daemon; the icon reflects daemon state and turns orange when it detects a run of silent clips.
yazses tray # show it now (blocks; Ctrl-C to stop)
yazses tray --background # launch it detached and returnMic changes from the menu take effect live (no restart). The daemon also launches
the tray automatically when a desktop is present — yazses tray just shows it right now.
"Quit tray" closes the icon but leaves dictation running (use "Stop daemon" to stop it).
Needs a system tray host — on GNOME that's the AppIndicator extension (standard on
Ubuntu); disable auto-launch with yazses features disable tray.
The friendly switchboard for turning capabilities on and off — no config-file
editing. Bare yazses features lists every capability grouped by category,
showing whether each is on/off, its toggle name, what enabling it downloads, and an
advice tier.
The DOWNLOAD column is what a fresh install fetches in total — the whole
dependency closure, so cocktail reads as ~3.1 GB rather than as the one package
you type, plus any model files the capability downloads the first time it runs,
so read-back reads as ~352 MB rather than as its 12 MB of packages. Blank means
nothing to download, which is true of most capabilities. See
Install only what you need.
Three states, not two:
| Badge | Meaning |
|---|---|
● ON |
On, and this build acts on it. |
○ off |
Off. |
◌ set |
Your config turns it on, but nothing in this build reads it — it is doing nothing. Clear the key with yazses features disable <name>. |
◌ only appears if you have one. It is not reachable from yazses features enable,
which refuses a capability that is planned — designed, not yet wired; it comes from
a config seeded by a version that predates that refusal, and it stayed invisible
because both facts (your setting, and whether anything reads it) were printed side by
side without ever being compared. Such a capability is not counted in a group's
(n/m on) tally, because it is not doing anything — but it does still show under
--on, which asks what your config turns on rather than what is running.
Options (on the list view):
| Option | Effect |
|---|---|
--on |
Show only capabilities that are currently ON. |
--tier <tier> |
Filter by advice tier: core, on, rec, opt, exp. |
--category / -c <name> |
Filter by category name (partial, case-insensitive), e.g. access. |
Subcommands:
| Command | Description |
|---|---|
yazses features info |
Describe every capability — name, what it does, a usage example (the full catalogue; pipe to less). |
yazses features info <name> |
Describe one capability — what it does, a usage example, and how to toggle it. |
yazses features enable <name> |
Turn a capability on (writes your config), then yazses restart to apply. |
yazses features disable <name> |
Turn a capability off, then yazses restart. |
yazses features reset |
Restore every capability to the state a fresh install ships with. |
yazses features # every capability, grouped, + advice
yazses features --on # show only what's currently enabled
yazses features --tier rec # show only the recommended tier
yazses features --category Multilingual # show one category
yazses features info # describe ALL capabilities + usage examples
yazses features info reflow # describe one + show a usage example
yazses features enable read-back # turn one on (use the TOGGLE NAME)
yazses features disable cocktail # turn one off
yazses features reset --dry-run # what a reset would change; writes nothing
yazses features reset # restore the defaults (asks first)
yazses restart # applyyazses features reset is the terminal half of the settings window's
Restore defaults button — the same operation, for a machine with no
graphical session, no PySide6, or a distribution too old to load Qt.
| Option | Description |
|---|---|
--dry-run |
List what would change and write nothing. |
--yes / -y |
Skip the confirmation prompt. |
--no-install |
Don't auto-install optional deps for what it turns back on. |
It writes only the capabilities that are off their default, so your config file and its comments are not churned to change three lines — and it resets the feature switches only. Your hotkey, microphone, vocabulary and every hand-edited setting are left exactly as they are.
Each row shows an advice tier:
Tier (--tier) |
Meaning |
|---|---|
core |
Always on (e.g. Dictation core) — can't be toggled. |
on |
recommended (on by default) — shipped on; keep it (Voice commands, Mid-Thought Undo, overlay). |
rec |
recommended — safe and useful, worth enabling (e.g. Dysfluency-Friendly if you stutter). |
opt |
optional — enable only if you want that capability (Punch-In, Prosody Ink, Read-Back, …). |
exp |
experimental — not advised yet — known rough edges (Cocktail Filter, Glance-Type). Refused unless you pass --force. |
Experimental features are guarded: yazses features enable cocktail prints why
it's not advised and exits; add --force to override.
The complete catalogue lives in the Feature Reference.
The same switchboard as a window: every capability as a checkbox, grouped by
the same categories yazses features prints, with its advice tier and a one-line
description underneath.
yazses settings # open the window (blocks until you close it)Checking a box stages the change; Apply writes them all, then restart the
daemon (yazses restart) to pick them up. The window is generated from the same
feature registry as the CLI, so the two can never disagree — and it honours the
same rules: core and planned — designed, not yet wired features are shown but
not clickable, and an experimental one asks for confirmation before it is staged.
Three things it gives you that a plain checkbox list cannot:
-
A filter box. Matches the name, the toggle name, the category and the description, so
stutterfinds Dysfluency-Friendly.on:/off:andtier:recmirror the flags above. Filtering is visibility only — Apply and Restore defaults still act on every capability. -
Help on every option. Hover a row, or click its ? button, for the same card
yazses features infoprints — what it does, when you'd want it, an example, the exact config keys ticking it writes, and any packages it installs. The ? button exists beside the tooltip rather than instead of it: hover is unreachable by keyboard, unavailable on touch, and never announced by a screen reader. -
Restore defaults. Puts every switch back to the state a fresh install ships with. It stages the change and names every capability it would touch first, so nothing is written until you press Apply — and it never turns an experimental capability on, because those are by definition not the advised set.
yazses features resetis the same operation in a terminal.
It needs a graphical session. On a headless box or a bare SSH session it says
so and points you at yazses features / yazses features reset instead of
failing at Qt.
Run the daemon in the foreground, logging to the console — useful for
debugging. This is the entry point yazses start supervises in the background.
| Command | Description |
|---|---|
yazses quickstart |
New here? Start here. A 3-step, machine-tailored getting-started guide (read-only). |
yazses doctor |
Health check: what's OK / missing, ending in a one-line verdict. |
yazses verify |
Record, transcribe and prove dictation works end to end; names the first broken link. |
yazses report |
Write a redacted diagnostic file you can attach to an issue. Nothing is uploaded. |
yazses autostart |
Start YazSes automatically at login, so it survives a reboot. |
yazses mic-level |
Measure your room and your voice, and put the VAD threshold between them. |
yazses logs |
Show the daemon's diagnostic log (metadata only unless log_level is DEBUG). |
yazses setup |
Provision all Linux runtime requirements so dictation works out of the box. |
yazses enroll |
Accessibility enrollment wizard: calibrate VAD thresholds to your voice. |
yazses enroll-voice |
Record an encrypted speaker voiceprint (for Cocktail Filter + Voiceprint Mind). |
yazses model |
Manage the SLM intent-routing models (list / download). |
yazses vocab |
Manage your personal dictionary (words STT mis-hears). |
yazses acronyms |
Manage a persistent acronym glossary and expand acronyms in text. |
yazses wordgoal |
Track words written against a writing goal, across invocations. |
yazses cliphistory |
A persistent clipboard history you can recall by voice-style reference. |
yazses outline |
Build a nested outline incrementally and render it to Markdown/OPML. |
yazses srs |
Capture facts as flashcards and schedule reviews (SM-2). |
yazses hotkey |
Show or change the key you hold to talk. |
yazses audio |
See and pin the input microphone (fixes a mic that silently switches). |
yazses gaze |
Aim dictation with your gaze — type into whichever pane you look at. |
Looks at what's already set up (prerequisites, whether the daemon is running, the
speech model, your hotkey) and prints exactly what to do next (setup → start →
hold the key), plus handy follow-ups. Safe to run anytime — it changes nothing.
yazses quickstart # the 3 steps to get dictating, tailored to your machineReports the installed version and daemon status (PID/state/model), then verifies
platform, keyboard-capture and microphone permissions, which input device the
hotkey binds to (flags a virtual injector device that would make the hotkey
dead), the session type (X11/Wayland) and its injection tools, injection
readiness + ydotoold status, the STT model and model cache, the config dir,
the active config + hotkey summary, and any configured extras (EMG port, prosody).
Each line is OK / WARN / FAIL / SKIP, and it ends with a one-line
verdict: ✓ everything's good, ▲ only optional warnings remain, or ✗ N
problems to fix — each with the concrete next command.
Options: --mic (also record a short ambient clip and warn if room level
meets/exceeds accessibility.vad_threshold).
yazses doctor # run this first if dictation isn't working
yazses doctor --mic # also sample the mic and compare it to the VAD gate!!! note "The Injection line names the backend that will actually be used"
It reads [injection] backend rather than only probing the session, so a machine
configured with backend = "clipboard" reports clipboard — not the tool that auto
would have picked. Before v2.30 the check looked only at the session type and the
installed tools, so it could report xdotool (X11) while the daemon was pasting.
A setting that **cannot** be honoured here gets its own `Injection setting` warning
instead of quietly doing nothing — `backend = "wtype"` on an X11 session is the case
that prompted it, since `wtype` speaks the Wayland virtual-keyboard protocol and has
no effect anywhere else.
doctor proves the prerequisites — a mic exists, xdotool is installed, the model is
cached. All of those can pass while dictation still produces nothing, because the silence
gate can sit above your voice, the model can return empty text, or the injector can be
aimed at a window that ignores synthetic keys.
verify runs the real chain — capture → silence gate → transcription → optional
injection — and reports each link. It stops at the first failure rather than cascading, so
you are told the one thing to fix instead of four consequences of it, each with the command
that fixes it.
Options: --seconds/-s (recording length, default 3), --type (also type the
transcript into the focused window).
yazses verify # speak for 3s; proves the pipeline end to end
yazses verify --seconds 5 # record for longer
yazses verify --type # also inject the result, testing the last link too [OK] Capture: recorded audio from the input device
[OK] Signal: level 0.0352 clears the gate (0.0020)
[OK] Transcription: produced 2 word(s)
✓ Dictation works end to end on this machine.
The Signal line also says when your voice only just cleared the gate. That is the one
regime where the transcription step can pass on a sentence you never said, so the level is
reported as a multiple rather than left for you to divide:
[OK] Signal: level 0.0052 clears the gate (0.0040) — but only just (1.3x). Speech
normally sits well clear of it; noise this close is what the model answers with
a confident invented sentence. If the transcript below is not what you said,
raise the gate with `yazses mic-level --set` before suspecting the microphone.
verify will not guess whether the words were invented — it cannot, and you can,
instantly, because they are on the screen.
It does ask a question that can be answered, about the recording rather than the
transcript: a speech detector runs on the captured audio, and when it finds no speech
anywhere, verify stops there instead of decoding near-silence into a confident word:
[OK] Signal: level 0.0059 clears the gate (0.0040) — but only just (1.5x)…
[FAIL] Speech: the level cleared the gate but a speech detector found no speech
anywhere in the recording — so whatever the model returns next is invented,
not heard. Something is being captured; it is not your voice.
That is a room with nobody speaking. Before this, the same recording printed
[OK] Transcription: heard "You" and a tick. The detector ships inside faster-whisper,
so it downloads nothing and sends nothing; where it cannot run, verify behaves exactly
as it did.
Collects a diagnostic bundle locally — versions, daemon state, your settings with paths, identifiers and anything you typed yourself replaced, and the tail of the metadata-only log. Your dictated text is never included, and the learning corpus is reported by size and never opened.
Nothing is uploaded, ever. The file is written, its path printed, and it is yours to read before deciding whether to attach it to an issue.
Settings whose values come from a small published set are kept in full — your hold-to-talk
key and command key are twelve fixed names that yazses hotkey show already prints, and
hiding them would remove the one comparison that matters: the bundle also records the key
the running daemon is listening on, and the two disagreeing is what explains a hotkey
that suddenly does nothing after a config change without a restart. Anything that is a
path, address, token or free text is still replaced.
Every string a config file can hold is classified as one of those three — an identifier,
your own prose, or a value from a published set — and a new setting fails the build until
someone decides which it is. Before that, the decision was a key-name regex plus a list of
the fields anyone had thought of, and [macros] author — whose comment in the source reads
"value substituted for ${author}" — was not on it. It came out as
<redacted> Seyedkazemi Ardebili: the account name matched, the surname did not, and the
marker made the field look handled. [filters.disfluency] llm_endpoint was the other one;
host, address and port were all filtered and "endpoint" was simply never spelled.
The daemon's live status is filtered by those same three rules, and used not to be. It is the part of the bundle that describes what YazSes is doing right now — its state, model, uptime, microphone, recent outcome counts — and one of its fields is the staged buffer, which is the text you have dictated and not yet committed, verbatim. Staged mode exists so you can read text before it is typed, so that field holds a sentence exactly when you are in the middle of one. It is now reported by length rather than content; the word and burst counts beside it are kept, because "the buffer has 22 words stuck in it" is the diagnostic part. The rest of the status keeps its values with your home directory and account name replaced — which microphone is in use, or which path an error names, is usually the whole answer to a bug, so those are redacted rather than blanked.
Options: --output/-o (where to write it), --print (print instead of writing),
--log-lines (how much log tail to include, default 200).
yazses report # writes ~/.local/share/yazses/yazses-report.json
yazses report --print # inspect it without writing a file
yazses report -o /tmp/r.json # choose the pathRuns YazSes at login so it is already there when you reach for the key. Works for every
install method — pipx, uv tool, pip, apt — by writing a systemd user service aimed at
this install, and rewriting it if an upgrade moves the binary.
The service restarts YazSes if it crashes (verified: killed outright, back within about five seconds) and gives up after five failures in a minute, so a genuinely broken machine leaves a diagnosable state instead of a spin loop.
yazses autostart enable # install + enable the login service
yazses autostart status # will YazSes be running after the next reboot?
yazses autostart disable # stop launching it at loginyazses doctor also reports this as a Starts at login check.
Records twice — the room while you stay quiet, then your voice while you speak —
reports both levels against the vad_threshold in config.toml, and recommends a
threshold that sits between them. Use it when dictation logs
Silent audio -- discarding.
The second recording is what makes the answer trustworthy. With one recording the command cannot tell speech from room tone (measured on a real corpus, no acoustic property of a single clip separates the two populations), so a quiet room produced a confident recommendation below its own noise. When the two recordings are less than 3x apart there is no gate that sits above the room and below the voice, and the command says so rather than writing a number.
If a daemon is running and is gating at a different threshold — which is what any
change without a yazses restart leaves behind, since the daemon reads the value once
at start — the command says so and names both numbers. Without that, the number it
prints describes the file rather than the process actually discarding your speech.
Options: --set (write the recommended threshold to config.toml in place,
comments preserved) · --seconds / -s N (record for N seconds instead of 4).
yazses mic-level # measure and recommend a threshold
yazses mic-level --set # measure and write it to config.toml
yazses mic-level -s 6 # record for 6 seconds instead of 4Print the diagnostic log — metadata only, never your dictated text, at the
default [general] log_level = "INFO". See Diagnostic log format
for what DEBUG adds.
Options: --lines / -n N (show the last N lines; default 40) · --path
(print the log file path and exit — ~/.local/state/yazses/log/daemon.log).
A log record is not always one line — an exception is a header plus its traceback. If
the last N lines would begin part-way through one, the window is moved to the record
boundary so the output never opens on an orphaned stack fragment, and the first line
says how it was adjusted. Expect a few lines more or fewer than N when that happens.
yazses logs # last 40 log lines
yazses logs -n 100 # last 100 lines
yazses logs --path # just print the log file pathLinux provisioning, one command. Installs the audio + injection system
packages (libportaudio2, xdotool, ydotool, wtype, xclip,
wl-clipboard), adds you to the input group (needed for the hotkey and for
ydotool's /dev/uinput access), and on Wayland sets up + enables the ydotoold
user service (required for injection on GNOME/KDE Wayland, where wtype is
blocked). Idempotent — only fixes what's missing. Finishes by printing a numbered
"finish installing" checklist of the steps only you can do (join the input
group with sudo usermod -aG input $USER, log out and back in, calibrate your
voice, then yazses start) and offers to run the mic calibration for you.
Options: --dry-run (show what it would install/change — plus the same
finish-installing checklist — without making any changes).
yazses setup # install deps, join input group, set up ydotoold
yazses setup --dry-run # preview the changes + checklistAccessibility enrollment wizard. Records ~20 short utterances to derive
vad_threshold and min_silence_ms values tuned to your voice and microphone,
then writes them to config.toml.
Can set a too-high threshold in a noisy room — verify with
yazses mic-level.
yazses enroll # calibrate the mic/VAD thresholds to your voiceRecords a short sample of your voice, computes a speaker embedding, and stores it
encrypted on this machine (never leaves it). Needed by Cocktail Filter and
Voiceprint Mind. Requires [voiceprint] enabled = true and the voiceprint extra
(uv sync --extra voiceprint). Run once; re-run to re-enroll.
yazses enroll-voice # record a sample → save your speaker voiceprintManage the Tier 2 SLM intent-routing models.
!!! note "Tier 2 is opt-in"
Downloading a model does not switch Tier 2 on. Point `[commands] slm_model_path`
at the GGUF and restart the daemon; until then Tier 1 decides every utterance,
which is the default and costs no inference. The command is also the right way
to pre-fetch a *speech* model behind a firewall.
| Command | Description |
|---|---|
yazses model list |
List available SLM models and their download status. |
yazses model download <model_id> |
Download a GGUF model for Tier 2 SLM intent routing. |
yazses model list # show SLM models + which are downloaded
yazses model download qwen2.5-0.5b # download an SLM for intent routingWords STT keeps mis-hearing (names, jargon, acronyms) are primed into Whisper's
initial_prompt so they're spelled right. Stored at
~/.config/yazses/vocabulary.txt. No restart is needed — the daemon re-reads it on
every burst. yazses doctor shows what actually reaches the recogniser.
| Command | Description |
|---|---|
yazses vocab add <word> ... |
Add one or more words/names to your dictionary. |
yazses vocab list |
Show every word in your dictionary. |
yazses vocab remove <word> |
Remove a word. |
yazses vocab export |
Print the dictionary (stdout by default, so it pipes). |
yazses vocab import <file> |
Merge entries from a file, or - for stdin. |
yazses vocab add YazSes # add one word/name
yazses vocab add Kubernetes kubectl # add several at once
yazses vocab list # show the dictionary
yazses vocab remove kubectl # drop a word
yazses vocab list # check what is in the dictionaryA persistent {ACR: full name} glossary stored at ~/.config/yazses/acronyms.json.
yazses acronyms expand rewrites text so each known acronym is spelled out on its
first occurrence (Full Name (ACR)) and contracted afterwards — fully offline.
| Command | Description |
|---|---|
yazses acronyms add <ACR> <full form> |
Register/replace an expansion. |
yazses acronyms list |
Show the stored glossary. |
yazses acronyms remove <ACR> |
Drop an entry. |
yazses acronyms expand [text] |
Expand acronyms in TEXT (or stdin) on first use. |
yazses acronyms add API "Application Programming Interface"
yazses acronyms expand "The API and the API"
# -> The Application Programming Interface (API) and the APIMoving it between machines. The dictionary is the main lever you have over recognition of your proper nouns, package names and acronyms — and without this, every new machine starts that work from zero.
yazses vocab export # print it; redirect or pipe as you like
yazses vocab export -o vocab.txt # or save it
yazses vocab import team-jargon.txt # merge someone else's list into yours
yazses vocab import - # read from stdin
cat vocab.txt | ssh workstation 'yazses vocab import -'Merging is the default and de-duplicates (case-insensitively): repeated
imports would otherwise grow the file and dilute the prompt, and every entry is
primed into the decoder — prompt length is not free. --replace discards your
existing dictionary and asks before doing so (--yes skips the question).
The format is one entry per line; # comments and blank lines are ignored, so a
shared domain vocabulary can explain itself. Export → import → export is
byte-identical, which makes it safe to keep in a dotfiles repo and diff.
A running word count persisted at ~/.config/yazses/wordgoal.json, so it accumulates
across invocations (dictate or pipe text in and watch progress toward a target).
| Command | Description |
|---|---|
yazses wordgoal add [text] |
Add TEXT (or stdin) to the running count; show progress. |
yazses wordgoal status |
Show the current count and goal progress. |
yazses wordgoal goal <n> |
Set a target word count (0 clears it). |
yazses wordgoal reset |
Zero the count (keeps the goal). |
yazses wordgoal goal 500
yazses wordgoal add "the paragraph I just wrote"
yazses wordgoal status # -> N of 500 words — M to go.A newest-first, de-duplicated, capped clipboard history stored at
~/.config/yazses/cliphistory.json. recall resolves a spoken-style reference to one
entry — fully offline.
| Command | Description |
|---|---|
yazses cliphistory add [text] |
Remember TEXT (or stdin). |
yazses cliphistory list |
Show history, newest first. |
yazses cliphistory recall <query> |
Print the entry a reference points to. |
recall understands url/link, email, ordinals (last, second, …),
first/oldest, and number N; it defaults to the most recent entry.
yazses cliphistory add "https://example.com"
yazses cliphistory recall "the last url" # -> https://example.comBuilds a nested outline across invocations (state at ~/.config/yazses/outline.json)
and renders it to Markdown or OPML — fully offline.
| Command | Description |
|---|---|
yazses outline add <text> |
Add an item after the cursor at the current level. |
yazses outline indent |
Nest the last item one level deeper. |
yazses outline promote |
Move the last item one level shallower. |
yazses outline render [--format markdown|opml] |
Render the outline. |
yazses outline clear |
Start a fresh outline. |
yazses outline add "Chapter 1"
yazses outline add "Section A"
yazses outline indent
yazses outline render # -> - Chapter 1 / (indented) - Section ACapture "remember that X is Y" facts as cloze flashcards (stored at
~/.config/yazses/srscap.json) and schedule reviews with the SM-2 algorithm — offline.
| Command | Description |
|---|---|
yazses srs capture [text] |
Detect a fact in TEXT (or stdin) and store it as a card. |
yazses srs list |
Show the deck and each card's next-review interval (days). |
yazses srs review <n> --grade <0-5> |
Grade recall; <3 lapses, higher grades lengthen the interval. |
yazses srs capture "remember that the capital of France is Paris"
yazses srs review 1 --grade 5 # -> review again in 1 day(s)Moving your dictionary to another device. Your dictionary and settings are
plain files under ~/.config/yazses/, so they move with a simple copy — no export
step:
# on the new device, after installing YazSes:
mkdir -p ~/.config/yazses
scp olddevice:~/.config/yazses/vocabulary.txt ~/.config/yazses/ # the dictionary
scp olddevice:~/.config/yazses/config.toml ~/.config/yazses/ # hotkey, VAD, etc.
yazses restartThe opt-in learning corpus (~/.local/share/yazses/) is not part of this — it
is encrypted on-device data and is intentionally not portable (see the
privacy statement).
| Command | Description |
|---|---|
yazses hotkey show |
Show the current dictation key, the command key (if any), and the choices. |
yazses hotkey set <key> |
Change the key you hold to dictate (e.g. right_ctrl), then yazses restart. |
yazses hotkey command <key> |
Set a dedicated command key, or off to disable it. Then yazses restart. |
Choices: auto (the shipped default — the usual key for your OS), right_alt,
left_alt, right_option, left_option, right_ctrl, left_ctrl,
right_shift, left_shift, right_meta, left_meta, space. Prefer a
dedicated modifier so it doesn't collide with normal typing.
right_option / left_option are the macOS names for the alt keys and mean the
same physical key, so one config file behaves the same on every OS. The command
key accepts everything except auto, which means "the platform's default
dictation key" and would land on the dictation key itself.
The same list is a dropdown in the Settings window.
yazses hotkey show # print the keys + the choices
yazses hotkey set right_ctrl # hold Right-Ctrl to dictate
yazses hotkey command right_ctrl # dictate on right_alt, commands on right_ctrl
yazses hotkey command off # remove the command key
yazses restart # apply!!! warning "yazses restart is not optional, and the CLI now says so"
The daemon reads `[hotkey] key` **once, when it starts**, and never again. Changing
the key without restarting leaves the file and the running process disagreeing, and
holding the key you just set does nothing at all — with no error anywhere, because
nothing failed.
`yazses hotkey show` and `yazses quickstart` both ask the running daemon what it is
actually listening on and warn when the two disagree; `quickstart` goes further and
tells you to hold the key that **works right now**, not the one in the file.
`yazses doctor` turns its `Hotkey` row to `[WARN]` for the same reason, and its
closing summary — the line most people actually act on — says
*"Dictation will not work until you restart"* rather than filing it under the
optional warnings next to a missing accessibility package.
```text
Hold-to-talk key: right_alt (dictation)
Command key: right_ctrl (force command mode)
⚠ config says 'right_alt' but the running daemon is listening on 'right_ctrl' —
holding right_alt will do nothing. A hotkey change does not reach a daemon that
is already running. Fix: yazses restart
```
Dedicated command key (force command mode). By default one key does both jobs: you hold the dictation key, speak, and YazSes auto-detects whether your phrase was a command ("save", "undo") or text. Binding a second key removes the ambiguity — while you hold it, everything you say is parsed as a command and never typed as literal text (an unrecognised phrase is simply ignored):
- The command key must be different from your dictation key.
- Holding the dictation key still works exactly as before (text, with command auto-detection).
- Holding the command key: "save" → Ctrl+S even though it would normally be text; "hello there" → ignored (no command matched), nothing typed.
See the voice command reference below for the phrases.
| Command | Description |
|---|---|
yazses audio devices |
List capture devices (● = OS default, ★ = pinned). |
yazses audio use <name> |
Pin the input mic by name (substring), then yazses restart. |
yazses audio use --clear |
Unpin — follow the OS default input again. |
yazses audio status |
Show pinned vs OS-default mic + live capture health. |
yazses audio devices # which mics exist, and which one is in use
yazses audio use "AT Translated" # pin the built-in laptop mic by name
yazses audio use --clear # go back to following the OS default
yazses restart # applyWhy pin a mic. By default capture follows the OS default input device. Plugging in a USB-C monitor, a dock, or a headset can make the OS silently re-pick that as the default input — often a dead or very quiet endpoint — and dictation "stops working" with no error (every clip is discarded as silence). Two things fix this:
- Pin your real mic with
yazses audio use <name>. The name is a case-insensitive substring, re-resolved on every recording, so it keeps working even if a hotplug renumbers devices. It prints the device the name resolved to — a loose substring is only useful if you can see what it caught. If several devices answer to the name it refuses and lists them, because the pin would otherwise select whichever the sound system happens to enumerate first, which is the thing pinning exists to rule out. And if the name is a route (default,pipewire,sysdefault) it says so: the device behind a route can change without the name changing, so such a pin holds nothing. - The mic-change guard (on by default,
yazses features disable mic-guardto turn off) watches for the default input changing and for a run of bursts that produced no text — whether they were discarded as silence or reached the model and decoded to nothing, which from your side is the same event. When it sees trouble it auto-heals — switching capture back to the last mic that worked — and shows a desktop notification with Re-calibrate / Pin this mic / Ignore buttons.
!!! warning "On Linux, the device-change half often cannot fire — pin instead"
The guard spots a switch by comparing the default device's **name** over time. On
ALSA and PipeWire the default is usually a routing *alias* called `default`, which
forwards to whichever device is current: the microphone behind it changes and the
name does not. So on a typical Linux desktop that half compares `default` with
`default` for ever.
The streak half is unaffected — it counts outcomes, not names — but it reacts after
the fact. **Pinning a real device is what makes a silent switch impossible rather
than merely detectable.**
`yazses audio status` and `yazses doctor` name the microphone actually behind the
alias, and its volume, so you can see what you are really recording:
```
OS default: default
→ Raptor Lake-P/U/H cAVS Digital Microphone (volume 65%)
```
If dictation goes quiet after changing your audio setup, run yazses audio status (or
yazses status) to see the live capture device and whether clips are being discarded.
| Command | Description |
|---|---|
yazses gaze calibrate |
Calibrate the webcam so your gaze maps to screen zones. |
yazses gaze status |
Show whether look-to-pane is ready: deps, desktop backend, calibration. |
Requires [gaze] enabled = true, an X11 session with xdotool, and a webcam. The
webcam gaze deps (mediapipe + opencv) are installed automatically on first run
into the running environment — no manual pip install (pass --no-install to skip,
or install them up front with yazses features enable gaze --force). You look at a
few on-screen points to fit the gaze→screen mapping; frames are used in-RAM during a
hold only — never stored or sent.
yazses gaze calibrate # fit the webcam gaze → screen-zone mapping
yazses gaze status # deps, X11/xdotool, and whether a calibration exists| Command | Description |
|---|---|
yazses inject <text> |
Type text into the focused window without recording (tests the injector). |
yazses say <text> |
Speak text aloud with the built-in offline voice. |
yazses overlay |
Run the sonar voice-activity overlay in the foreground (preview/debug). |
yazses reflow [text] |
Reflow a monologue into a bulleted outline — fully offline. |
yazses table [text] |
Turn spoken rows into delimited (CSV) lines — fully offline. |
yazses shellpipe [text] |
Render a spoken pipeline into a shell command (printed, never run). |
yazses braille [text] |
Translate text to Unicode Braille (UEB subset) — fully offline. |
yazses case [text] |
Recase text to a naming convention (snake/kebab/camel/…) — fully offline. |
yazses screenplay [text] |
Format dictated lines as Fountain screenplay markup — fully offline. |
yazses findreplace <command> |
Apply a spoken 'replace X with Y' edit to text — fully offline. |
yazses chords [text] |
Turn a spoken key chord into injectable key combos — fully offline. |
yazses wordfind <description> |
Reverse dictionary: describe a word, get ranked candidates. |
yazses cite <query> --bib <file> |
Resolve a spoken 'author year' reference against a .bib. |
yazses slotfill <text> --slot ... |
Extract structured fields from an utterance by a schema. |
yazses punch-in |
Correct the last dictation by re-speaking just the wrong phrase. |
yazses transcribe <file> |
Transcribe an audio file to text — fully offline. |
yazses meeting |
Record a whole meeting hands-free → speaker-labelled transcript + optional minutes. |
yazses test |
End-to-end self-test: confirm the injector works without speaking. |
Types YazSes OK into the focused window (no speaking) so you can confirm
injection works. Focus a text editor first.
yazses test # focus an editor first, then watch for 'YazSes OK'Inject text into the focused app without recording — the quickest way to test the injection backend.
yazses inject "hello world" # type it into the focused windowIt uses the backend your config selects, and prints the concrete one it chose:
$ yazses inject "hello world"
Backend: XdotoolInjectorUntil v2.30 it — along with yazses test and yazses verify --type — always built
auto, whatever [injection] backend said, and printed the selector class
(LinuxInjector) rather than the backend behind it. All three are commands whose job is
to test the injector, so all three could pass on a backend the daemon would not use.
yazses status names the same thing, and the two can now be compared.
On Wayland the default (auto) types via ydotool, which works in every app —
editors, browsers, and terminals — and never touches your clipboard. If you'd
rather paste (instant, but a no-op in terminals where Ctrl+V is literal, and
overwrites the clipboard), set the backend:
[injection]
backend = "clipboard" # "auto" (default) | "type" | "clipboard" | "wtype"Or per-run without editing config: YAZSES_INJECTOR=clipboard — an exported override
wins over backend = "auto" and is left alone. Run yazses restart after changing the
config. yazses status, yazses inject and yazses verify --type all report and use
the backend actually in use.
Speak arbitrary text aloud through the offline TTS voice (Read-Back Loop). Routes
through the running daemon so it reuses the loaded TTS backend. Requires
[tts] enabled = true (install the voice with uv sync --extra tts).
yazses say "hello there" # speak text aloud via offline TTSRuns the sonar voice-activity overlay in the foreground (preview/debug) — neon
rings near the cursor that pulse with your voice while dictating. Normally the
daemon auto-launches it ([overlay] enabled = true); the direct console script
yazses-overlay does the same thing. PySide6 ships in the base install and is
bundled in the snap, so it works out of the box. For true see-through rings on X11
you need a compositor (e.g. picom).
yazses overlay # preview the sonar rings in the foregroundAuto-launch is on by default; the daemon only spawns the overlay when a
display (DISPLAY/WAYLAND_DISPLAY) is present and PySide6 is installed. See
the Configuration Reference for the [overlay] keys
(style, position, react_to_voice, accent, size_px, fps,
cursor_offset_px).
One-shot, fully-offline text transforms. Each reads its TEXT argument, or
standard input when omitted (so you can pipe a transcript in). Nothing is
uploaded.
| Command | Options | Example |
|---|---|---|
yazses reflow |
— | yazses reflow "First, set up. Then test. I need to ship." |
yazses table |
--sep <char> (default ,) |
yazses table "row: Ada, 1815, London next row Bob, 1990, Paris" |
yazses shellpipe |
— | yazses shellpipe "list files then count lines" → ls | wc -l |
yazses braille |
--grade <1|2> (default 2) |
yazses braille "hello world" → ⠓⠑⠇⠇⠕ ⠺⠕⠗⠇⠙ |
yazses case |
--style <name> |
yazses case --style snake "myVariableName" → my_variable_name |
yazses screenplay |
— | yazses screenplay "scene: interior coffee shop, day" → INT. COFFEE SHOP - DAY |
yazses findreplace |
--in <text> |
yazses findreplace "replace every cat with dog" --in "the cat" → the dog |
yazses chords |
— | yazses chords "press control shift P" → ctrl+shift+p |
yazses wordfind |
--limit <n>, --lexicon <file> |
yazses wordfind "happy accident" → serendipity |
yazses cite |
--bib <file> (req), --style <latex|plain|apa> |
yazses cite "vaswani 2017" --bib refs.bib → \cite{vaswani2017} |
yazses slotfill |
--slot NAME:after=… | NAME:choices=… (repeatable) |
yazses slotfill "priority high" --slot priority:after=priority → {"priority": "high"} |
-
reflowsplits on sentence boundaries, strips leading discourse markers (first,then,finally, …) and leading fillers (um,so basically,you know,well, … — the same list the dictation disfluency filter uses), and turns action phrases (I need to,to do,follow up) into- [ ]checkboxes. Speech stacks them, so stripping repeats:so um basically I need to update the docsgives- [ ] I need to update the docs. Only leading fillers count —we ship it so people can use itkeeps itsso. -
tablesplits cells on commas/semicolons or the wordand; rows split onnext rowor newlines; a leadingrow:/entry:/record:marker is stripped. -
shellpiperecognises stages likelist files,filter for X,count lines,sort,unique, and prints the pipeline for you to review — it never executes anything; it exits non-zero (emitting nothing) if a stage isn't recognised. -
brailletranslates to Unicode Braille;--grade 1is uncontracted. -
caserecases text to a naming convention (snake,kebab,camel,pascal,title,sentence,upper,lower,constant); with no--styleit detects a spokenmake this … casecommand and recases the remainder (the colon is optional). -
screenplayformats each line as Fountain:scene interior/exterior <place> <time>→INT./EXT. …,<Name> character <dialogue>→ a character cue,transition cut to→CUT TO:; other lines become smart-quoted action lines.Every separator here is optional, because a colon, a comma and a pair of parentheses are three things you cannot dictate — and this command is for drafting by voice.
scene: interior coffee shop, dayandscene interior coffee shop daygive the same result. The parenthesised(character)form stays exact; the spoken one is only read as a cue when the name is short and does not begin with a determiner, sothe character walks instays an action line. -
findreplaceparsesreplace every/first X with Y(addcase-sensitive) and applies it to--in/stdin; exits non-zero if the command can't be parsed. -
chordsparses a spoken chord (modifiers + a named/F/char key + an optional repeat liketwice) intoctrl+shift+p-style combos, one per line. -
wordfindranks a small built-in lexicon by content-word overlap with your description; extend it with--lexicon(a JSON{word: definition}object). -
citeparses a local.bib, matches a spokenauthor yearquery, and formats the entry (--style latex|plain|apa); exits non-zero if nothing matches confidently. -
slotfillextracts structured fields from one utterance: each--slotis eitherNAME:after=kw1,kw2(capture the token after a keyword) orNAME:choices=a,b,c(pick the first enum member present); prints a JSON object of the matched fields.
cat notes.txt | yazses reflow # reflow a piped transcript
yazses table --sep ';' "a, b next row c, d" # semicolon-separated CSV
yazses braille --grade 1 "abc" # Grade 1 (uncontracted)Turn a spoken git command into a git command, fully offline. It always prints the resolved command and how to undo it, which is the point — you see exactly what you are about to run before anything happens.
Options: --run (execute instead of only printing) · --yes (confirm a
destructive command so --run will run it).
$ yazses gitvoice "commit with message fix the parser"
git commit -m 'fix the parser'
undo: git reset --soft HEAD~1
$ yazses gitvoice "discard changes in src"
git checkout -- src
undo: recover via: git reflog / your editor's local history (uncommitted changes are gone unless stashed first)Reads the TEXT argument, or standard input when omitted, and exits non-zero
if it cannot parse the utterance — so a misheard command produces an error, not
a plausible wrong command.
A branch you name reaches the command. push to main renders
git push origin main, not a bare git push — which would push whichever branch
you happen to be standing on. Say the remote too (push to upstream develop) and it
is used; say only a branch and origin is assumed, which fails loudly on a repo
whose remote is called something else rather than quietly pushing the wrong ref.
Say the punctuation in a branch name. feature/login and fix-tray-crash are
both ordinary conventions and neither can be dictated — nothing turns the silence
between two words into a / or a - — so you say the separator:
| You say | You get |
|---|---|
create branch feature slash login |
git checkout -b feature/login |
create branch fix dash tray dash crash |
git checkout -b fix-tray-crash |
push to feature hyphen login |
git push origin feature-login |
slash, dash, hyphen, underscore and dot all work, on every command that
reads a branch name. They apply only where a ref is read: commit with message fix the dash in the title keeps its word.
Destructive commands are never run without --yes, even with --run.
Force-push, hard reset, branch -D and discarding uncommitted changes all
require it. Misrecognition is a fact of dictation, so the one class of command
you cannot undo is the one that asks twice:
yazses gitvoice "force push" --run # refuses: destructive, needs --yes
yazses gitvoice "force push" --run --yes # actually runs itRe-speak just the wrong phrase to correct the last dictation burst. The daemon
records a short window, aligns the respoken phrase against the last burst it
typed, then deletes that burst and retypes it corrected. Because pure respeak
fixes only ~35% on the first try (Suhm 2001), the alignment surfaces the top
candidates rather than silently splicing. Requires [punch_in] enabled = true.
Options: --dry-run (list candidate spans without editing, so you can confirm
first) · --choose / -n N (apply the candidate at rank N; 0 = best,
default).
yazses punch-in # re-speak the phrase; correct the best match
yazses punch-in --dry-run # list candidate spans without editing
yazses punch-in --choose 1 # apply the 2nd-ranked candidateTranscribe an existing audio file offline and write a sidecar text file next
to it (talk.mp3 → talk.txt), using the same local faster-whisper engine as
live dictation — no cloud, no network, no account. CLI-only; it doesn't touch the
daemon, hotkey, or your dictation config. Accepts wav/mp3/m4a/ogg/flac/opus/mp4
and most other ffmpeg-decodable media.
Options:
| Option | Effect |
|---|---|
--format / -f <fmt> |
Output format: txt (default) | md | srt | vtt | json. srt/vtt add timestamps; json is lossless (per-word timestamps + speaker). |
--out / -o <path> |
Write to an explicit path instead of the sidecar default. |
--model <name> |
Override the STT model for this run (e.g. small.en = more accurate, slower). Defaults to your [stt] model. |
--language <lang> |
Spoken language as a Whisper code — en (default), fa, de; empty auto-detects; translate renders any-language audio into English. A non-English code needs a multilingual model (drop the .en suffix). |
--diarize / --no-diarize |
Tag who said what with local speaker models (needs the diarization extra). |
--speakers <N> |
Force an exact speaker count (0 = auto-detect). |
--max-speakers <N> |
⚠ On the shipped sherpa diarizer this forces an exact count, it does not cap one: 6 on a three-person recording manufactures six by splitting real speakers apart. 0 (default) auto-detects. |
--min-speakers <N> |
⚠ Ignored by the default sherpa diarizer — only the pyannote backend reads a lower bound, and that is an optional extra (diarization-pyannote, plus a gated Hugging Face model). The command says so before transcribing rather than silently ignoring the floor. |
--names "Alice,Bob" |
Comma list mapped to speakers in order of first appearance. |
--rename speaker_0=Alice |
Explicit speaker→name map, repeatable. |
--download-models |
Fetch the ~45 MB sherpa diarization models, then exit (no transcription). |
It tells you when the transcript is not trustworthy, rather than reporting success over it:
- Nothing recognised — music, silence, or speech in a language an English-only model
cannot read produce an empty file. The command says so and names the causes you can
act on, the useful one being the
.enmodel, which you can neither see nor guess from a blank file. - Audio with no signal in it — a muted microphone, an input held by another application, or capture pointed at the wrong device. Speech models answer silence with a confident invented word rather than with nothing (two seconds of digital silence decodes to "You"), so an empty-transcript check cannot catch this. The check is on the input instead: audio carrying no signal cannot contain speech. The transcript is still written — you may want to see what was invented — but it is not presented as a result.
- A file that is not audio, or is missing — reported as such, with the formats that do work, instead of an ffmpeg command line.
yazses transcribe talk.mp3 # → talk.txt beside it
yazses transcribe talk.mp3 -o notes.txt # choose the output path
yazses transcribe lecture.mp3 --format srt # subtitle file with timestamps
yazses transcribe talk.mp3 --model small.en # more accurate, slower
yazses transcribe talk.fr.m4a --language fr # transcribe French as French
yazses transcribe talk.fr.m4a --language translate # any language → English
yazses transcribe mtg.m4a --diarize # tag speakers: 'Speaker 1: …'
yazses transcribe mtg.m4a --diarize --speakers 3 # exact speaker count
yazses transcribe mtg.wav --diarize --names 'Alice,Bob' # name them in order
yazses transcribe mtg.wav --diarize --rename speaker_0=Alice # name one speaker
yazses transcribe mtg.m4a --download-models # fetch diarize models, then exitNotes:
- Speaker tags need the diarization extra:
uv sync --extra diarization(sherpa-onnx — CPU-only, no PyTorch, no GPU, no account). Without it,--diarizedegrades to a plain transcript and tells you. - Speaker naming is opt-in and private. With
--diarizeand an enrolled voiceprint (yazses enroll-voice), your own voice is auto-labelled "You"; everyone else isSpeaker Nunless you name them. No new data is stored and no one is ever enrolled automatically — speaker voiceprints are biometric data and stay encrypted on this machine. You are responsible for having permission to record and transcribe the audio.
Record a whole meeting without holding a key, then get a speaker-labelled
transcript (and optional minutes) — all offline. While recording it streams a
rolling live transcript for the status view; at stop it runs an accurate batch
diarization post-pass over the full recording (reusing the same local sherpa speaker
models as yazses transcribe --diarize, so no new dependency), then optionally
generates minutes with a local LLM. Speakers are told apart by voice embeddings +
clustering, not pitch. Off by default — enable with yazses features enable meeting.
| Command | Effect |
|---|---|
yazses meeting start |
Start recording hands-free (no key to hold). Requires [meeting] enabled = true. |
yazses meeting stop |
Stop; run the diarization post-pass and write the speaker-labelled transcript (and notes if enabled). |
yazses meeting status |
Show the running meeting (elapsed + live transcript), or recent meetings. Says so plainly when Meeting Mode is off. |
yazses meeting list |
List stored meetings on this machine (no daemon required). Marks any meeting whose transcript failed its quality check ⚠ BAD TRANSCRIPT. |
yazses meeting summary [<id>] |
Show what a meeting produced, where each file is, and what not to trust. Omit the id for the most recent meeting. Exits 2 when the transcript is not a usable record. |
yazses meeting relabel <id> |
Fix speaker labels and re-render: --merge SPEAKER_2=speaker_1 folds clusters, --rename speaker_1=Alice names one (both repeatable); --format/-f picks the re-render format (default md). |
yazses meeting notes <id> |
Generate minutes (summary, decisions, action items) from a stored transcript. Needs [meeting] notes = true and a local notes_model GGUF; runs locally (slow on CPU). |
yazses meeting recover <id> |
Re-run the post-pass on a meeting whose finalize never completed or whose transcript failed its quality check. The recording is kept whenever either is true, so a crash — and a decode that collapsed — leaves the whole meeting on disk; this transcribes, diarizes and names it and writes the same outputs. Never deletes the recording, and archives the previous outputs to attempts/<n>/ rather than overwriting them. --force re-runs a meeting that finished cleanly. |
yazses meeting enroll <id> --speaker <cluster> --name <name> |
Enroll one speaker from a stored meeting as a named voiceprint, so they are auto-named next time. Both flags are required. Needs the recording to still exist — i.e. the meeting ran with [meeting] retain_audio = true. |
yazses features enable meeting # turn it on (writes [meeting] enabled = true)
yazses meeting start # begin hands-free capture
yazses meeting status # elapsed + live rolling transcript
yazses meeting stop # diarize + write the labelled transcript
yazses meeting list # see stored meetings
yazses meeting list --json # machine-readable array
yazses meeting relabel <id> --rename speaker_1=Alice --merge speaker_2=speaker_1
yazses meeting notes <id> # local-LLM minutes (needs notes_model)
yazses meeting enroll <id> --speaker speaker_1 --name Alice # name them for good
yazses meeting recover 20260812-140310 # a meeting that crashed: re-run the post-pass
yazses meeting summary # where are my notes? (most recent meeting)
yazses meeting summary 20260812-140310meeting summary is the answer to "I had a meeting — where is the transcript?". It prints
the folder, every artefact in it with what that artefact is for, and — first, before the
file list — anything about the result that should stop you reading it as a record:
Meeting 20260826-100205
Duration: 41m 39s
⚠ transcript.md collapsed into a repetition loop — do NOT read it as a record.
· one phrase is 97% of the transcript (a healthy decode stays under 20%)
· the live transcript of the same audio holds 4553 words against this pass's 284 (16.0x)
Speakers: not separated (diarization off or unavailable)
Files:
✅ live-transcript.md — live transcript streamed during the meeting (4553 words) — READ THIS ONE
transcript.md — batch transcript ⚠ UNRELIABLE
transcript.json — word-level timings + speakers (machine-readable)
live.jsonl — raw live-decode records
quality.json — decode-quality metrics for this meeting
audio.wav — recording KEPT (re-run: `yazses meeting recover 20260826-100205`)
Folder: /home/you/.local/share/yazses/meetings/20260826-100205
The same text is written into the meeting folder as summary.md at stop, and shown as a
desktop notification when the post-pass finishes — a meeting has no key held and no
terminal watched, and its post-pass ends long after you have walked away.
Every meeting is transcribed twice. live-transcript.md is the rolling decode written
line by line during the meeting; transcript.md is the accurate batch pass at stop. They
are independent, and neither is ever deleted. When they disagree sharply, the batch pass has
usually collapsed — a well-known decoder failure where it emits one phrase for the rest of
the file — and the live transcript is the better record. quality.json records the numbers
behind that judgement for every meeting, healthy or not.
live-transcript.md is appended to as each utterance is decoded, so it can be opened,
previewed or tailed mid-meeting rather than only existing once the post-pass has finished:
yazses meeting start # prints the path, and the tail command for it
tail -f ~/.local/share/yazses/meetings/<id>/live-transcript.mdyazses meeting status shows the last few utterances and names the same file, which holds
all of them. The machine-readable live.jsonl remains the source of truth: the whole file
is re-rendered from it at stop and at every meeting recover, so a write torn by a crash
is repaired rather than left in the copy you read. Turn the incremental write off with
[meeting] live_markdown = false — the file is then written once at stop, as before.
A transcript that fails its quality check changes three things: the recording is kept
regardless of [meeting] retain_audio, the minutes pass is skipped (a summary of invented
words reads exactly like a real one — meeting notes --force overrides), and the meeting is
offered for meeting recover even though it finished.
Each stored meeting lists as id · length · speakers · directory, and the length is the
column that tells them apart — a real meeting sits among the accidental starts that a
hands-free capture inevitably collects:
20260819-033515 11s not diarized …/20260819-033515
20260814-065156 26s not diarized …/20260814-065156
20260803-095635 2h 14m not diarized …/20260803-095635
20260710-212029 56s not diarized …/20260710-212029
not diarized means speaker labelling was never attempted, which is a different statement
from "nobody spoke". A meeting whose post-pass never finished shows unfinished instead,
with the recovery command beneath it.
meeting status reports whether the feature is on before anything else. With
[meeting] enabled = false — the default — nothing is recorded, so it says that and names
the command that turns it on, rather than reporting on speaker labels for a feature that
will never produce a transcript. Meetings recorded earlier are still listed, as history.
meeting recover exists because the recording outlives a failure. audio.wav is deleted
only once the post-pass that consumes it has succeeded, so a crash, a kill, a machine that
slept, or an out-of-memory notes model all leave the whole meeting on disk. meeting list
marks those unfinished and prints the exact recover command for them:
20260812-140310 unfinished ~/.local/share/yazses/meetings/20260812-140310
⚠ did not finish — the whole recording was kept. `yazses meeting recover 20260812-140310` re-runs the post-pass on it.
…and 41 line(s) of live transcript are readable in ~/.local/share/yazses/meetings/20260812-140310/live.jsonl
The live.jsonl is the rolling transcript the live decode happened to catch — readable
immediately, never diarized. The recording is the accurate source, and recover is the only
thing that turns it back into a meeting. It never deletes the recording (on a retry it is the
only copy) and it refuses a meeting that already finished rather than overwriting a good
transcript.
One difference from the live path: recover does not apply enrolled voiceprints, so speakers
come back as speaker_1, speaker_2 — naming them needs the daemon's cipher and embedder. Run
yazses meeting relabel <id> --rename speaker_1=Alice afterwards.
meeting enroll is the one that carries across meetings: relabel fixes the labels
on one transcript, while enroll saves that speaker's voiceprint so the next
meeting names them on its own. The voiceprint is encrypted and never leaves the
machine (ADR-011/012), it enrols only the speaker you name, and it needs the audio
— which is deleted at stop unless [meeting] retain_audio = true.
Notes:
- Reuses the diarization extra: the speaker post-pass uses the same sherpa-onnx
models as
transcribe --diarize(uv sync --extra diarization— CPU-only, no PyTorch/GPU/account). Key[meeting]knobs:retain_audio(defaultfalse— the recording is deleted after transcription),live_transcript,diarize,min/max_speakers,cluster_threshold,name_from_voiceprints(auto-label your own enrolled voice),output_format, and thenotes*fields. See configuration.md for the full list. - Minutes are opt-in and local.
notesstays off until you setnotes = trueand pointnotes_modelat a local GGUF — nothing is sent anywhere. - You are responsible for having consent to record and transcribe the meeting.
Fuzzy-matches a spoken query against the files in a directory and opens the best match with your desktop's default handler.
Arguments: QUERY (required) — the spoken query.
Options: --dir / -d <path> (directory to search, default .) ·
--yes / -y (launch immediately, without the confirmation prompt).
yazses fileopen "the quarterly budget spreadsheet"
yazses fileopen "meeting notes from march" --dir ~/Documents
yazses fileopen "readme" -y # skip the confirmationWithout -y it shows the match and asks first — a fuzzy match on a misheard
query can otherwise open the wrong file.
Jumps to a symbol or a line in the active editor, via the configured LSP editor bridge.
Arguments: TARGET (required) — e.g. line 240, function tokenize, main.
yazses jump "line 240"
yazses jump "function tokenize"Requires a reachable editor bridge. ([commands] lsp_enabled and lsp_editor are
not read by anything — this command contacts the editor directly whichever way
they are set; the keys are reserved for an editor-context prompt that is designed
and not wired.) Start Neovim with nvim --listen. VS Code is not an option
here: its bridge supplies dictation context only, and no YazSes VS Code extension
is published. Without a live bridge there is no real cursor to move, so the command
reports that rather than guessing:
$ yazses jump "line 240"
Editor bridge not reachable. Start Neovim with `nvim --listen` (yazses reads $NVIM).
VS Code cannot be used for jumping: its bridge supplies dictation context only and
has no cursor motion, and no YazSes VS Code extension is published.
Hold the command key (or, with auto-detect, the dictation key) and say one of these. Phrases are matched case-insensitively and trailing punctuation is ignored, so "Save file." works the same as "save". Spelled-out numbers ("delete the last three words") are accepted.
| Say | Does |
|---|---|
| "undo" / "undo that" · "undo N times" | Ctrl+Z (× N) |
| "save" / "save file" | Ctrl+S |
| "copy" / "copy that" · "cut" · "paste" | Ctrl+C / Ctrl+X / Ctrl+V |
| "comment" / "comment this" | toggle comment (Ctrl+/) |
| "select all" · "select N lines" · "select to end" | selection |
| "delete the last word" · "delete the last N words" | delete word(s) |
| "delete the last line" · "delete the last N lines" | delete line(s) |
| "new line" / "enter" / "press enter" | Enter |
| "tab" · "escape" · "press backspace" | Tab / Esc / Backspace |
| "page up" · "page down" | Page Up / Page Down |
| "go up / down / left / right" (or "move …") | arrow keys |
| "end of line" · "beginning of line" | End / Home |
| "go to line N" | jump to line N |
| "go to function NAME" · "go to class NAME" · "open file NAME" | editor navigation |
| "run the tests" · "run the build" · "run that" | run in terminal |
| "run CMD" (anything else) | run in terminal — command key only, see below |
| "rename this to NAME" | rename symbol (F2) |
!!! warning "run <anything> needs the command key"
`run CMD` types the command **and presses Return**, so it executes. Its grammar
is `^run (.+)$` — which every ordinary sentence beginning with "run" satisfies,
because the sentence *is* the argument. Dictating "run the numbers again before
Friday" would have executed it in whatever window had focus.
So the open-ended form only fires while the **command key** is held
(`[hotkey] command_key`). Without it, the words are typed like any other
dictation. The closed-vocabulary forms — "run the tests", "run the build",
"run that" — are unambiguous whole utterances and still work without it.
Every other command is recoverable by retyping. This one is not, which is why
it is the only one gated this way.
Don't see a command you want? Tell us — the grammar is easily extended.
Natural-language commands beyond this fixed list go through the Tier 2 SLM router,
which is off unless you point [commands] slm_model_path at a local GGUF model and
have llama-cpp-python installed. It is consulted only when the Tier 1 grammar decides
an utterance is plain dictation, and its answer is taken only above
[commands] slm_confidence_threshold.
Enable with yazses features enable voice-punctuation, then speak the name of a
mark to insert it (say yazses restart after enabling):
| Say | Inserts |
|---|---|
| "comma" | , |
| "period" / "full stop" | . |
| "question mark" | ? |
| "exclamation mark" | ! |
| "colon" / "semicolon" | : / ; |
| "new line" | line break |
| "new paragraph" | blank line |
| "tab key" | tab |
Example: "the tests pass comma ship it period" → the tests pass, ship it.
It is off by default because these words also appear in ordinary speech.
On by default ([revise] enabled = true). Say "scratch that" (or "delete
that" / "no scratch that") as a whole utterance to delete the last thing YazSes
typed — it issues backspaces, so it works in any text field, and a buffer ledger
ensures it never deletes more than YazSes injected. Saying the phrase inside a
sentence ("scratch the surface") does not trigger it.
Off by default ([timeline] enabled = true). Where Mid-Thought Undo removes
the last thing typed, the timeline steps back through what YazSes typed by an
amount you name — and can step forward again.
| You say | Effect |
|---|---|
| "undo" · "undo that" | step back one burst |
| "undo two words" | step back 2 words |
| "undo the last sentence" | step back 1 sentence |
| "undo 3 bursts" | step back 3 bursts |
| "undo everything" | step back over the whole session's injections |
| "redo" | step forward again |
Counts accept digits or the words one–ten; above ten (MAX_REPEAT) the
utterance is not a command and is typed instead.
This is distinct from the undo voice command, which sends Ctrl+Z to the
application (see the table above). The timeline knows what YazSes itself
injected; Ctrl+Z is whatever the focused application decides it means.
Like "scratch that", the grammar is anchored at both ends, so ordinary speech containing the word is typed, not executed:
"undo two words" → steps back two words
"click undo" → typed
"I need to undo that" → typed
"press control z to undo" → typed
That anchoring is not a nicety. "undo" is an ordinary English word, and a pattern that merely has to end the utterance matches all three of the lines above — which would silently delete a user's text instead of typing their sentence.
| Command | Description |
|---|---|
yazses remote <host> |
Forward voice typing to a remote host over SSH. |
yazses-agent --listen <port> |
Run the remote injection agent on the remote host. |
Expose YazSes to another agent over MCP, on stdin/stdout.
Not a service you leave running: an MCP client spawns it as a child process and talks to it over pipes. There is no port, no bind address, and nothing another machine can reach — the same structural property as YazSes's own Unix-socket IPC, which is why ADR-020 chose stdio over HTTP for a daemon that holds a live microphone.
Point an MCP client at it:
{"command": "yazses", "args": ["mcp-server"]}| Tool | What it does | Offered when |
|---|---|---|
transcribe(path, diarize=false) |
Turn an audio or video file into text, entirely on this machine. Nothing is uploaded. | always |
ask_human(question, timeout_s) |
Ask you a question out loud and return your spoken answer. | [mcp] ask_human = true |
ask_human is the reason this exists. An agent stuck on a decision only a person
can make otherwise has to put text on a screen and wait to be noticed; speaking is
the cheapest interrupt you can service, because it needs neither your eyes nor your
hands.
It is off by default and not even listed until you enable it — a tool that is offered and always refuses teaches a model to stop calling it. When on:
[mcp] ask_human_per_hour(default 3) is a budget shared by every caller, so it limits interruptions to you rather than per agent. Nothing a caller does earns another slot.- Nothing is spoken while you are dictating; the question waits, and costs the agent nothing.
- The caller is named in what is spoken.
- Your answer goes back to the agent and is never typed into the window you had open.
Speak on your local machine and have the text typed into the focused app on a remote SSH host (via a reverse tunnel).
Options: --port / -p <n> (SSH port; default 22) · --key-file / -i <path> (SSH private key) · --stop (disconnect the active remote session).
yazses remote dev.example.com # forward voice typing over SSH
yazses remote dev.example.com -p 2222 # non-default SSH port
yazses remote dev.example.com --stop # disconnect the sessionOn the remote host, run the injection agent that receives the keystrokes:
yazses-agent --listen 9875The self-improvement loop is opt-in, local, and encrypted. It requires
[learning] enabled = true (off by default; ADR-012). All data stays on the
machine, encrypted at rest with a machine-bound key.
| Command | Description |
|---|---|
yazses mark-wrong |
Flag the last dictation as a misrecognition (a learning signal). |
yazses coach |
Show private speaking-style analytics (filler rate, WPM, vocabulary). |
yazses recall [words…] |
Search your past dictations (Spoken Recall). |
yazses scratch [list|clear] |
Show or clear ambient "note to self …" scratch notes. |
yazses tune |
Analyse the corpus and propose accuracy improvements. |
yazses corpus |
Inspect or clear the local learning corpus. |
Flag the last dictation as a misrecognition. Routes through the running daemon so
the flag lands on the event it just captured. Options: --correction / -c "..." (attach what you actually said).
yazses mark-wrong # flag the last dictation as wrong
yazses mark-wrong -c "kubernetes pod" # flag it and attach the correct textSpeaking-style analytics from your recent dictations — filler-word rate,
words-per-minute, vocabulary variety. Reads only your local encrypted corpus.
Options: --limit / -n N (how many recent dictations to analyse; default
100).
yazses coach # stats from your recent dictations
yazses coach -n 200 # analyse the last 200Search your past dictations for words, or show the most recent with no query.
Requires [learning] enabled = true and [recall] enabled = true. Reads the
local encrypted corpus only.
yazses recall kubernetes deploy # search past dictations for those words
yazses recall # show your most recent dictationsShow (list, the default) or clear ambient scratch notes captured by saying
"note to self …" in command mode. Requires [recall] scratch = true; notes are
stored in a plain local file.
yazses scratch # list your ambient note-to-self notes
yazses scratch clear # delete all scratch notesAnalyse the captured corpus and print proposed config diffs (vocabulary,
vad_threshold, model, disfluency rules, SLM few-shots). Each proposal is checked
against a recent held-out slice of the corpus and labelled validated (N/M
held-out) / unverified / unvalidated (corpus too small) (ADR-014);
corroborated proposals are listed first. Dry-run by default — changes nothing.
Options: --apply (review each proposal interactively and write approved ones
to config.toml, comments preserved) · --retranscribe / --no-retranscribe
(re-transcribe captured audio with a larger model to find errors; on by default —
skip for a faster run that uses only flagged/edited signals) · --limit N
(re-transcribe only the N most recent clips).
Re-transcription is the slow step by a wide margin. Budget roughly 10 seconds per
clip on a laptop CPU — measured at 9.6 s with small.en — so a corpus sitting on the
default max_corpus_mb = 500, about 1,500 clips, is a multi-hour run rather than an
hour. Clip count is the right predictor, not total audio: every clip is padded to 30 s
before the encoder sees it, so the cost per clip barely varies with its length. The
command reports its progress and, once it can measure a rate on your machine, how long is
left. --limit is the middle ground between that and skipping the
pass altogether: the recent clips are the ones recorded with the model, microphone
and threshold you use now, which is what tuning is trying to improve.
yazses tune # dry-run: print proposed config changes
yazses tune --apply # review and write approved changes
yazses tune --limit 200 # only the 200 most recent clips (much faster)
yazses tune --no-retranscribe # skip the slower re-transcription passInspect or clear the local learning corpus.
| Command | Description |
|---|---|
yazses corpus status |
Show corpus location, event/discard/flag counts, size, and date range. |
yazses corpus forget --minutes N / -m N |
Delete events captured in the last N minutes (e.g. after dictating something private). |
yazses corpus destroy --i-mean-it |
Irreversibly wipe the corpus (database + audio clips). --i-mean-it is required. |
yazses corpus status # location, counts, size, date range
yazses corpus forget -m 10 # delete the last 10 minutes of events
yazses corpus destroy --i-mean-it # irreversibly wipe the whole corpusstatus reports the size against [learning] max_corpus_mb. Eviction runs in sweeps —
when the daemon starts, then every 200 captures — so a size above the cap is normal
between sweeps and does not mean eviction has failed. forget and destroy are immediate.
A sweep drops the oldest events, reclaims the freed database pages, and stops as soon as
a round frees nothing — so a cap set below what the database can shrink to leaves the
corpus trimmed as far as it goes rather than emptied. If status keeps reporting a size
above the cap after a sweep, that is what happened; yazses logs says so, and raising
max_corpus_mb or running yazses corpus destroy are the two ways out.
Every deletion path zeroes the bytes it frees, so a deleted transcript is not left
readable inside corpus.db; forget also compacts the file, clearing anything earlier
versions left behind.
Most capabilities are toggled with yazses features enable/disable <name> (see
Daemon → yazses features above) and configured in
config.toml. This section is a quick snapshot of the most-asked-about knobs; the
complete, generated config surface is in the
Configuration Reference, and the full capability catalogue
is in the Feature Reference.
Off by default. Enable in config.toml, then define triggers in a sibling
macros.toml:
# config.toml
[macros]
enabled = true
author = "Your Name" # value substituted for ${author}
path = "macros.toml" # relative to the config dir, or absolute# macros.toml — speak the trigger alone to expand it
[[macro]]
trigger = "license header"
type = "text"
text = "# SPDX-License-Identifier: MIT\n# Copyright (c) ${date} ${author}\n"
[[macro]]
trigger = "try except"
type = "snippet" # ${cursor} marks where the caret lands after expansion
snippet = "try:\n ${cursor}\nexcept Exception as exc:\n raise"- Matching is whole-utterance exact (case/whitespace/trailing-punctuation insensitive): saying "license header" on its own fires; saying it inside a sentence does not, so macros never trigger mid-dictation.
- A macro takes precedence over a built-in command of the same phrase.
- Placeholders:
${cursor}(snippet caret, first occurrence),${date}(YYYY-MM-DD),${time}(HH:MM),${author}(from config),${clipboard}. Unknown${...}tokens are left literal. No shell/command execution. type = "actions"(OS/app key chains) is parsed but dormant in this release.
Enable with [punch_in] enabled = true; run with yazses punch-in (above).
[punch_in]
enabled = true
min_score = 0.5 # minimum difflib similarity to surface a span
max_candidates = 3
record_seconds = 4.0 # re-record window for the respoken phraseOff by default, batch dictation only. A long inter-word pause becomes a paragraph
break; with format = "markdown" and the prosody extra
(uv sync --extra prosody → parselmouth) vocal emphasis becomes bold.
[prosody]
enabled = true
format = "markdown" # none | markdown
pause_paragraph_ms = 700
emphasis_enabled = true
emphasis_sensitivity = 0.65
max_latency_ms = 150 # above this, logs a warning and degrades to pause-onlyClean stuttered / dysarthric dictation. Off by default. One switch enables the
collapse pass (b-b-because → because, the the the → the, sooo → so)
plus wider onset padding — while protecting proper nouns, code identifiers, URLs,
intentional hyphenation (re-read), and emphasis (very very). Hold-to-talk, so
endpointing is unchanged (ADR-015). Fully offline, no model training.
[accessibility]
dysfluency_friendly = true # collapse pass + wider onset padding
# Fine-grained knobs (set individually instead of the preset if you prefer):
[filters.disfluency]
collapse_repetitions = true
collapse_prolongations = true
prolongation_min_run = 3
repetition_max_fragment_len = 2Off by default. After each dictation YazSes speaks the transcript back so you can
verify by ear — useful eyes-free or with low vision. yazses say "text" speaks
arbitrary text on demand. Install the offline voice with uv sync --extra tts
(Kokoro-82M, Apache-2.0).
[tts]
enabled = true
engine = "kokoro" # kokoro (default) | melo | kitten
voice = "default"
speed = 1.0
max_readback_chars = 600 # longer bursts are truncated with "…"
[accessibility]
read_back = "final" # off (default) | final | confirm (P2)Off by default. The daemon predicts when you stop (stable confirmed prefix + trailing silence) and pre-warms the decode path to hide release latency. Pre-warm is harmless — the authoritative transcript still happens on real hold-release.
[endpoint]
enabled = true
prewarm = true
debounce_ms = 500 # anti-thrash between endpoint firesFour advanced features that personalize and focus recognition — all off by
default, fully local, each needing an optional extra and/or hardware
(mic/webcam) or a one-time training step. yazses doctor reports whether each
enabled feature's extra is importable. Plans: design/v2-cognitive-layer/.
Voiceprint Mind — personalize STT to your voice ([personalize]). P1 (now)
biases the recognizer toward your vocabulary so it spells your jargon and proper
nouns:
[personalize]
enabled = true
max_prompt_terms = 64
# lora = true # P2: opt-in nightly LoRA personal fine-tune (gated on a WER win)Cocktail Filter — ignore other voices ([cocktail]). Drops audio frames that
aren't you before transcription. Enroll once (yazses enroll-voice), then:
[voiceprint]
enabled = true # speaker embedder (uv sync --extra voiceprint)
[cocktail]
enabled = true # mode = "gate" (P1); "suppress" (P2) is gated on a model
target_threshold = 0.6 # higher = stricter "is this me?"Glance-Type — look at a pane to target it ([gaze]). Coarse webcam gaze picks
the screen zone/window your next dictation lands in. Needs a webcam + a one-time
yazses gaze calibrate:
[gaze]
enabled = true
zones = "grid3x3" # grid3x3 | grid2x2 | windows
camera_index = 0The camera is used in-RAM during a hold only — frames are never stored or sent.
Polyglot Switch — mixed-language dictation ([polyglot]). Transcribe speech
that mixes two languages (e.g. fa-en). Needs a trained code-switch adapter for
the pair; the routing is scaffolded and the adapter is gated on a held-out MER win.
[polyglot]
enabled = true
pair = "fa-en"
adapter_path = "" # path to the trained CS adapter; empty = dormantyazses logs shows lines like:
INFO yazses.core.daemon: Transcribed 2.1s audio in 480 ms (model base.en, level 0.0043)
INFO yazses.core.daemon: Injecting 24 chars, 5 words.
INFO yazses.core.daemon: Silent audio -- discarding (level 0.0009 < vad_threshold 0.0021; run 'yazses mic-level --set' to retune).
These are metadata only — audio level, latency, model, counts, and errors.
The actual transcript text is logged only when general.log_level = "DEBUG" in
config.toml.
