Metadata-Version: 2.5
Name: tacheles
Version: 0.1.0
Summary: Push-to-talk dictation for Linux that knows your technical vocabulary and learns the words it gets wrong. OpenAI or fully local.
Project-URL: Homepage, https://github.com/ErikEremenko/tacheles
Project-URL: Issues, https://github.com/ErikEremenko/tacheles/issues
Author: Erik Eremenko
License: MIT License
        
        Copyright (c) 2026 Erik Eremenko
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: accessibility,dictation,gnome,linux,openai,speech-to-text,wayland
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: X11 Applications :: GTK
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: openai<4,>=3.6
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Provides-Extra: indicator
Requires-Dist: pygobject>=3.46; extra == 'indicator'
Description-Content-Type: text/markdown

# tacheles

**Dictation that speaks your stack.** Push a key, talk, push again. The text lands where you were
typing. For developers who would explain the whole thing to their coding agent if writing it out
were not such an effort, and who are tired of transcription that mangles every identifier.

*Tacheles reden* is German, from the Yiddish **תכלית** *takhlis*, "purpose". It means talking
plainly and getting to the point. Pronounced **TAH-khe-les**.

---

### It knows your vocabulary

General speech models have never heard of your stack. tacheles has.

![Correcting technical vocabulary](docs/media/vocabulary.gif)

`fast API` → `FastAPI`. `red is` → `Redis`. `post gres SQL` → `PostgreSQL`. None of that is
guesswork. It sends your vocabulary with every request, then corrects what comes back against that
same list.

### It learns the rest

The jargon no model could guess, you teach once. It sticks, permanently, and it is applied
deterministically, not re-guessed each time.

```
tacheles fix 'P and PM' pnpm      # said once, corrected forever
tacheles review                   # confirm what it was unsure about
```

It also ships with corrections for manglings that no vocabulary hint fixes, because what the model
heard is nothing like the right word: `P and PM` for `pnpm`, `Griffana` for `Grafana`, `item
potent` for `idempotent`, `cloud code` for `Claude Code`. Those work on a fresh install, and your
own always take precedence.

Every automatic correction is inspectable, and anything it is not certain about is *suggested*
rather than silently applied. It never rewrites a real word behind your back.

### It speaks more than one language, mid-sentence

![Three languages in one sentence](docs/media/multilingual.gif)

English, German and Russian in a single breath, each kept in its own script, with no setting
required. Declaring the languages you mix is supported and harmless, but in testing here it made
no measurable difference: Russian came back in correct Cyrillic even when only English was
declared.

### It can run entirely on your machine

Point it at a local whisper.cpp, faster-whisper or Speaches server and **no audio leaves your
computer at all**. The vocabulary and learning layers work exactly the same, and they matter
more, because small local models are worse at rare terms rather than better.

```toml
[transcription]
backend = "local"
```

See [docs/LOCAL.md](docs/LOCAL.md) for setup, and for an honest account of what you trade away.

---

## Why not just use the dictation built into your editor?

Because it was not built for this. Dictation bundled into AI tools tends to transcribe
*everything*: every "um", every false start, every repeated word. You get a wall of text and then
have to edit it. And none of it knows what `pnpm` is.

tacheles takes the opposite position on both counts:

| | Built-in dictation | tacheles |
|---|---|---|
| Filler words | Transcribed verbatim | Dropped, while keeping how you actually talk |
| Your jargon | Guessed phonetically | Sent as vocabulary, then corrected against it |
| Getting it wrong twice | Every time | Teach it once, never again |
| Mixed languages | Usually one language wins | Each kept in its own script |
| Where your audio goes | Their servers | OpenAI, or nowhere but your own machine |

## Why this exists

The bottleneck in working with coding agents is not typing speed. It is the small, constant
reluctance to explain something properly.

You know the shape of what you want. Writing it out means four paragraphs of context: what the
code does now, what it should do instead, which edge case bit you last time, what not to touch.
Every one of those paragraphs has a cost, and when you are tired or in the middle of something
else you pay it by writing less. You send three lines instead of thirty, the agent guesses at the
rest, and you spend the afternoon correcting work that would have been right if you had just said
the whole thing.

Speaking it costs almost nothing. So you say the whole thing, first time.

That is the actual win, and it is not about words per minute. Building
[PolySimulator](https://polysimulator.com), a paper-trading platform for Polymarket prediction
markets, we run a lot of agents at once. The scarce resource is not keystrokes, it is attention:
holding the architecture in your head, reading what came back, judging whether it is right,
deciding what happens next. Composing precise English is a tax on exactly that attention. Paying
it ten times a day leaves less of you for the parts that actually need a person.

Which is what the name is about. *Tacheles reden* means saying the thing plainly and getting on
with it, with no throat-clearing and no working up to it. Say what you want; let the work start.

There was one thing standing in the way on Linux. Dictation here is bad. Browser microphone
buttons are flaky, desktop apps cannot type into other apps on Wayland, and local models either
want a GPU or mangle every technical term. Every tool that got this right turned out to be
macOS-only, which I discovered the hard way while looking for a name: the good ones were already
taken by macOS dictation apps.

And general transcription mangles developer speech specifically. It writes "P and PM" for `pnpm`,
"fast API" for `FastAPI`, "red is" for `Redis`, and turns a colleague's name into something
unrecognisable. When half your sentences carry identifiers, that is not a small annoyance, because
fixing the transcript costs more than typing it would have. The friction comes straight back, just
somewhere else.

So tacheles sends your vocabulary with every request, corrects what comes back against that same
vocabulary, and permanently learns whatever you tell it once. The point is that dictating stays
cheaper than typing all the way through, including the proofreading.

It is ~3,700 lines of Python that shells out to tools you already have (`pw-record`, `wl-copy`,
`notify-send`) and uses OpenAI, or a local Whisper server, for the hard part.

*Tacheles*, from the Yiddish **תכלית** *takhlis*, "purpose", is the German idiom for talking
plainly and getting to the point. *Tacheles reden.* Pronounced **TAH-khe-les**.

## Install

```bash
git clone https://github.com/ErikEremenko/tacheles && cd tacheles
./install.sh
```

`install.sh` creates a private virtualenv, installs tacheles into it, and registers the GNOME
shortcuts. Then give it a key and check the setup:

```bash
tacheles auth login      # stored in the GNOME keyring, or a 0600 file
tacheles doctor          # explains anything that will not work, and why
```

Your key never leaves your machine except in requests to OpenAI. Nobody involved in this project
receives it, and [docs/CREDENTIALS.md](docs/CREDENTIALS.md) shows you how to verify that rather
than take it on trust. It also covers using a secret manager (Bitwarden Secrets Manager, pass,
1Password, gopass) instead of storing the key at all.

`tacheles doctor` is the first thing to run whenever something misbehaves. It reports your
recorder, credentials, model, delivery method, keyboard layout and hotkeys in one screen.

## How the text reaches your app

This is the genuinely hard part on Wayland, which deliberately stops one app synthesising input
into another. tacheles has three modes, and **always writes the clipboard first** so a failure
downstream can never lose your transcript.

| Mode | What it does | Setup | Notes |
|---|---|---|---|
| `clipboard` *(default)* | Copies the text; you press Ctrl+V | none | Always works |
| `paste` | Copies, then synthesises Ctrl+V | `tacheles setup-paste` | Needs ydotool on GNOME |
| `type` | Types the text keystroke by keystroke | `tacheles setup-paste` | **US layout only**, see below |

**A warning about `type` mode.** ydotool emits raw US keycodes and ignores your active XKB
layout. If you have a non-US layout configured, typed text comes out mangled. tacheles detects this
and refuses rather than producing garbage. `paste` mode is unaffected, because only the Ctrl+V
chord travels through the keyboard, and the text itself goes via the clipboard.

## Seeing what it is doing

GNOME hides notification banners after a few seconds whatever expire-timeout you ask for, so a
notification cannot tell you a recording is still running. The optional top-bar indicator can:

```bash
tacheles install-indicator     # then log out and back in
```

It shows the state in the panel: microphone when idle, a red dot and a running timer while
recording, an amber icon while transcribing, a green tick when the text is ready. Clicking it
opens your recent transcripts; clicking one copies it again, and a middle click on the icon
copies the newest without opening anything.

That list matters more than it sounds. `wl-copy` holds the clipboard in a child process, so a
session restart empties the clipboard even though the transcript was produced correctly. Nothing
is ever lost, because it is in the history either way:

```bash
tacheles last --copy       # put the most recent transcript back on the clipboard
tacheles last -n 5         # or look at the last five
```

Audio cues are on by default. `tacheles sounds` plays each one, and they are configurable
under `[notify]`.

## Vocabulary profiles

Profiles live in `~/.config/tacheles/profiles/*.toml` and shadow the bundled ones.

```toml
name = "coding"
language = "en"
prompt = """
Technical software-development dictation. Keep technical terms in canonical casing.
"""
keywords = ["pnpm", "FastAPI", "PostgreSQL", "Grafana", "idempotent"]
```

`inherits = "coding"` merges another profile's keywords, so a profile of your own project's names
can extend the general technical vocabulary rather than repeat it.

```bash
tacheles profiles              # list them
tacheles toggle -p coding      # use one for a single dictation
```

## Safety and cost

- **Auto-stop.** A forgotten recording stops itself after `audio.max_seconds` (default 5 minutes)
  and transcribes what it captured. Set `max_seconds_action = "discard"` to spend nothing
  instead, or `max_seconds = 0` to disable.
- **Silence guard.** An all-silent recording is never uploaded, so a muted mic costs nothing.
- **Spend tracking.** `tacheles history --totals` shows minutes dictated and estimated cost.

## Commands

```
tacheles toggle [-p PROFILE]   start, or stop and transcribe
tacheles cancel                discard the current recording
tacheles status                is it recording?
tacheles transcribe FILE       transcribe an existing audio file
tacheles doctor                check everything
tacheles models                transcription models your key can use
tacheles history [--totals]    recent transcripts and spend
tacheles config edit           edit the config
tacheles install-hotkeys       register GNOME shortcuts
tacheles uninstall-hotkeys     remove them and restore GNOME's originals
```

## Hotkeys and your input-source switcher

GNOME ships `Super+Space` bound to *switch input source*. Rather than leaving you without a
layout switcher, `tacheles install-hotkeys` moves that binding to the Windows-style **Shift+Alt**
and records the original. `tacheles uninstall-hotkeys` puts it back exactly as it was.

Shortcuts are appended to your existing custom shortcuts, never overwritten.

## Support

The part that does the work is plain Linux. Recording goes through `pw-record`, `parecord` or
`arecord`, so PipeWire, PulseAudio and ALSA are all covered; the clipboard through `wl-copy`,
`xclip` or `xsel`, so Wayland and X11 both are; notifications through `notify-send`, which is a
freedesktop standard. Nothing in the recording, transcription or correction path needs GNOME, and
that is checked rather than assumed: the core runs in an environment stripped of D-Bus, `DISPLAY`
and `gsettings`.

What GNOME adds is convenience, and each piece degrades on its own when it is absent.

| | Status |
|---|---|
| Recording, transcription, vocabulary, corrections, clipboard | Works anywhere. Verified with no GNOME present |
| GNOME / Wayland | Everything, including hotkey installation and the top-bar indicator. Used daily by the author |
| GNOME / X11 | Should work; `xclip` and `xdotool` cover clipboard and pasting. Not tested |
| KDE, sway, Hyprland, others | Core works; bind the commands yourself, see [docs/HOTKEYS.md](docs/HOTKEYS.md). Not tested |
| Automatic paste-chord selection | GNOME only, because the extension is what can see the focused window. Falls back to the configured chord elsewhere |

"Not tested" means exactly that. It is expected to work and nobody has confirmed it. If you run
one of those, an issue saying so either way is genuinely useful.

## Configuration

`tacheles config init` writes a fully commented file with every default. Highlights:

```toml
[general]
profile = "coding"        # default vocabulary profile
output_mode = "auto"      # auto | clipboard | paste | type

[openai]
model = "gpt-transcribe"  # $0.0045/min; falls back if unavailable to your key

[audio]
max_seconds = 300         # auto-stop safety limit
silence_rms = 30          # below this, do not bother uploading

[postprocess]
enabled = false           # optional second pass to tidy punctuation
```

## How this was built

Written by [Erik Eremenko](https://github.com/ErikEremenko), largely with
[Claude Code](https://claude.com/claude-code) driving the keyboard. I am stating that plainly
because it is true and because you can see it in the commit history anyway. A project about
dictating to AI agents that hid its own use of them would be a bit ridiculous.

What that means for you as a user or contributor: every design decision here was tested against
this machine rather than assumed, and the awkward findings are documented rather than smoothed
over: the ~100 ms clipboard wait, the refusal to type on non-US layouts, the German compound word
that silently inverted meaning. Where something is unverified, it says so.

If you find something that was clearly never exercised, open an issue. That is the failure mode
worth watching for, and I would rather hear about it.

## License

MIT. See [LICENSE](LICENSE). Contributions welcome; see [CONTRIBUTING.md](CONTRIBUTING.md).
