tacheles 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. tacheles-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +19 -0
  2. tacheles-0.1.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
  3. tacheles-0.1.0/.github/workflows/ci.yml +29 -0
  4. tacheles-0.1.0/.gitignore +16 -0
  5. tacheles-0.1.0/CHANGELOG.md +42 -0
  6. tacheles-0.1.0/CONTRIBUTING.md +38 -0
  7. tacheles-0.1.0/LICENSE +21 -0
  8. tacheles-0.1.0/PKG-INFO +350 -0
  9. tacheles-0.1.0/README.md +303 -0
  10. tacheles-0.1.0/SECURITY.md +48 -0
  11. tacheles-0.1.0/data/tacheles.desktop +11 -0
  12. tacheles-0.1.0/docs/CREDENTIALS.md +180 -0
  13. tacheles-0.1.0/docs/FAQ.md +103 -0
  14. tacheles-0.1.0/docs/HOTKEYS.md +74 -0
  15. tacheles-0.1.0/docs/LAUNCH.md +75 -0
  16. tacheles-0.1.0/docs/LOCAL.md +71 -0
  17. tacheles-0.1.0/docs/media/README.md +14 -0
  18. tacheles-0.1.0/docs/media/multilingual.gif +0 -0
  19. tacheles-0.1.0/docs/media/multilingual.tape +22 -0
  20. tacheles-0.1.0/docs/media/vocabulary.gif +0 -0
  21. tacheles-0.1.0/docs/media/vocabulary.tape +38 -0
  22. tacheles-0.1.0/gnome-extension/tacheles@erikeremenko.github.io/extension.js +484 -0
  23. tacheles-0.1.0/gnome-extension/tacheles@erikeremenko.github.io/metadata.json +8 -0
  24. tacheles-0.1.0/gnome-extension/tacheles@erikeremenko.github.io/stylesheet.css +32 -0
  25. tacheles-0.1.0/install.sh +75 -0
  26. tacheles-0.1.0/llms.txt +57 -0
  27. tacheles-0.1.0/pyproject.toml +56 -0
  28. tacheles-0.1.0/scripts/gh-metadata.sh +16 -0
  29. tacheles-0.1.0/tacheles/__init__.py +4 -0
  30. tacheles-0.1.0/tacheles/__main__.py +3 -0
  31. tacheles-0.1.0/tacheles/audio.py +371 -0
  32. tacheles-0.1.0/tacheles/cli.py +922 -0
  33. tacheles-0.1.0/tacheles/config.py +392 -0
  34. tacheles-0.1.0/tacheles/correct.py +822 -0
  35. tacheles-0.1.0/tacheles/data/default_aliases.toml +62 -0
  36. tacheles-0.1.0/tacheles/doctor.py +215 -0
  37. tacheles-0.1.0/tacheles/history.py +106 -0
  38. tacheles-0.1.0/tacheles/keys.py +256 -0
  39. tacheles-0.1.0/tacheles/notify.py +171 -0
  40. tacheles-0.1.0/tacheles/output.py +477 -0
  41. tacheles-0.1.0/tacheles/profiles/coding.toml +42 -0
  42. tacheles-0.1.0/tacheles/profiles/general.toml +13 -0
  43. tacheles-0.1.0/tacheles/session.py +333 -0
  44. tacheles-0.1.0/tacheles/transcribe.py +456 -0
  45. tacheles-0.1.0/tests/conftest.py +51 -0
  46. tacheles-0.1.0/tests/test_audio.py +74 -0
  47. tacheles-0.1.0/tests/test_config.py +129 -0
  48. tacheles-0.1.0/tests/test_correct.py +459 -0
  49. tacheles-0.1.0/tests/test_keys.py +29 -0
  50. tacheles-0.1.0/tests/test_output.py +159 -0
  51. tacheles-0.1.0/tests/test_session_history.py +123 -0
  52. tacheles-0.1.0/tests/test_transcribe.py +245 -0
@@ -0,0 +1,19 @@
1
+ name: Bug report
2
+ description: Something does not work
3
+ body:
4
+ - type: textarea
5
+ id: doctor
6
+ attributes:
7
+ label: Output of `tacheles doctor`
8
+ description: This reports your compositor, audio backend and injection method. Redact nothing except your API key.
9
+ render: shell
10
+ validations: { required: true }
11
+ - type: textarea
12
+ id: what
13
+ attributes:
14
+ label: What happened, and what did you expect?
15
+ validations: { required: true }
16
+ - type: input
17
+ id: distro
18
+ attributes: { label: Distro + desktop + session (e.g. Fedora 44, GNOME 50, Wayland) }
19
+ validations: { required: true }
@@ -0,0 +1 @@
1
+ blank_issues_enabled: true
@@ -0,0 +1,29 @@
1
+ name: CI
2
+ on:
3
+ push: { branches: [main] }
4
+ pull_request:
5
+ jobs:
6
+ test:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ python-version: ["3.11", "3.12", "3.13"]
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ # The correction layer asks hunspell whether a word is real. Without a dictionary it
14
+ # cannot tell "Supervisor" from a mistranscription, so the tiering is untested.
15
+ # German and Russian too: the corrector refuses to auto-apply anything when a declared
16
+ # language cannot be spell-checked, so English alone leaves the multilingual path untested.
17
+ - name: Install spell-check dictionaries
18
+ run: sudo apt-get update -qq && sudo apt-get install -y -qq hunspell hunspell-en-us hunspell-de-de hunspell-ru wamerican
19
+ - uses: astral-sh/setup-uv@v5
20
+ - name: Set up Python
21
+ run: uv python install ${{ matrix.python-version }}
22
+ - name: Install
23
+ run: uv sync --extra dev
24
+ - name: Lint
25
+ run: uv run ruff check tacheles tests
26
+ - name: Format check
27
+ run: uv run ruff format --check tacheles tests
28
+ - name: Tests
29
+ run: uv run pytest -q
@@ -0,0 +1,16 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ build/
5
+ dist/
6
+ .venv/
7
+ venv/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ .mypy_cache/
11
+ *.wav
12
+ *.ogg
13
+ *.flac
14
+ .env
15
+ env
16
+ secrets/
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ First release. Everything below was tested on Fedora 44 with GNOME 50 on Wayland; other
6
+ combinations are supported but less exercised, and the README says which.
7
+
8
+ ### Dictation
9
+
10
+ - Push-to-talk recording via `pw-record`, `parecord` or `arecord`, whichever is present. Capture
11
+ starts about 65 ms after the hotkey, so the first word is not clipped.
12
+ - Transcription with OpenAI `gpt-transcribe`, falling back through `gpt-4o-transcribe`,
13
+ `gpt-4o-mini-transcribe` and `whisper-1` depending on what the key can reach. Request parameters
14
+ are built per model, because `keywords` and `languages` exist only on some of them.
15
+ - A local backend for whisper.cpp, faster-whisper-server, Speaches or LocalAI. No audio leaves the
16
+ machine and no API key is needed.
17
+ - Delivery to the clipboard, with optional automatic pasting. The paste chord is chosen from the
18
+ window that had focus when recording began, so terminals get Ctrl+Shift+V.
19
+
20
+ ### Getting your words right
21
+
22
+ - Vocabulary profiles sent to the API as `keywords`, with inheritance between profiles.
23
+ - Post-transcription correction against that same vocabulary: casing, near misses, and compound
24
+ terms the model split in two ("fast API" to FastAPI, "red is" to Redis).
25
+ - Learned aliases via `tacheles fix`, applied deterministically thereafter.
26
+ - A shipped set of corrections for manglings no vocabulary hint fixes ("P and PM" for
27
+ pnpm, "Griffana" for Grafana, "cloud code" for Claude Code). Your own override them.
28
+ - Corrections are tiered. A word hunspell recognises in any of your declared languages is never
29
+ rewritten unattended; it is suggested for `tacheles review` instead.
30
+ - Multilingual dictation, mixed within a sentence, each language kept in its own script.
31
+
32
+ ### Around the edges
33
+
34
+ - A GNOME Shell extension showing recording, transcribing and ready in the top bar, with a
35
+ transcript history you can copy from and a profile picker.
36
+ - Idempotent GNOME hotkey installation that appends to your existing shortcuts and moves
37
+ `switch-input-source` to Shift+Alt, restoring it on uninstall.
38
+ - An auto-stop watchdog, five minutes by default, so a forgotten recording cannot run on.
39
+ - A silence guard, calibrated to your room by `tacheles calibrate`, so a muted microphone costs
40
+ nothing.
41
+ - `tacheles doctor`, which explains anything that will not work and why.
42
+ - API key from the GNOME keyring, any secret manager via `api_key_command`, or a `0600` file.
@@ -0,0 +1,38 @@
1
+ # Contributing to tacheles
2
+
3
+ Thanks for helping out. tacheles is a small, dependency-light tool; please keep it that way.
4
+
5
+ ## Ground rules
6
+
7
+ - **Standard library first.** The only hard runtime dependency is `openai`. Anything else must be
8
+ optional and guarded behind a try/except import.
9
+ - **Shell out, don't bind.** We call `pw-record`, `wl-copy`, `ydotool` and friends as subprocesses.
10
+ This keeps the install trivial and avoids compiled dependencies.
11
+ - **Every external binary is optional.** If a tool is missing, degrade gracefully and say so in
12
+ `tacheles doctor`, and never crash.
13
+ - **No network in tests.** The OpenAI client is mocked. `pytest` must pass on a headless runner
14
+ with no audio device.
15
+
16
+ ## Development setup
17
+
18
+ ```bash
19
+ git clone https://github.com/ErikEremenko/tacheles && cd tacheles
20
+ uv sync --extra dev # or: python -m venv .venv && .venv/bin/pip install -e '.[dev]'
21
+ uv run pytest -q
22
+ uv run ruff check tacheles tests
23
+ ```
24
+
25
+ ## Adding support for another desktop
26
+
27
+ Text injection and hotkey binding are the two desktop-specific parts.
28
+
29
+ - Injection: add a class to `tacheles/output.py` implementing the `Injector` protocol
30
+ (`name`, `available()`, `type_text()`, `key_chord()`) and register it in `INJECTORS`.
31
+ - Hotkeys: add a backend to `tacheles/keys.py` implementing `install()` / `uninstall()` /
32
+ `available()`.
33
+
34
+ Please include what you tested it on, and add a row to the support table in the README.
35
+
36
+ ## Commit style
37
+
38
+ Short imperative subject lines (`add opus encoding`, `fix wav header repair on empty capture`).
tacheles-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Erik Eremenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,350 @@
1
+ Metadata-Version: 2.5
2
+ Name: tacheles
3
+ Version: 0.1.0
4
+ Summary: Push-to-talk dictation for Linux that knows your technical vocabulary and learns the words it gets wrong. OpenAI or fully local.
5
+ Project-URL: Homepage, https://github.com/ErikEremenko/tacheles
6
+ Project-URL: Issues, https://github.com/ErikEremenko/tacheles/issues
7
+ Author: Erik Eremenko
8
+ License: MIT License
9
+
10
+ Copyright (c) 2026 Erik Eremenko
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
29
+ License-File: LICENSE
30
+ Keywords: accessibility,dictation,gnome,linux,openai,speech-to-text,wayland
31
+ Classifier: Development Status :: 4 - Beta
32
+ Classifier: Environment :: X11 Applications :: GTK
33
+ Classifier: Intended Audience :: End Users/Desktop
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Operating System :: POSIX :: Linux
36
+ Classifier: Programming Language :: Python :: 3 :: Only
37
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
38
+ Classifier: Topic :: Utilities
39
+ Requires-Python: >=3.11
40
+ Requires-Dist: openai<4,>=3.6
41
+ Provides-Extra: dev
42
+ Requires-Dist: pytest>=8; extra == 'dev'
43
+ Requires-Dist: ruff>=0.6; extra == 'dev'
44
+ Provides-Extra: indicator
45
+ Requires-Dist: pygobject>=3.46; extra == 'indicator'
46
+ Description-Content-Type: text/markdown
47
+
48
+ # tacheles
49
+
50
+ **Dictation that speaks your stack.** Push a key, talk, push again. The text lands where you were
51
+ typing. For developers who would explain the whole thing to their coding agent if writing it out
52
+ were not such an effort, and who are tired of transcription that mangles every identifier.
53
+
54
+ *Tacheles reden* is German, from the Yiddish **תכלית** *takhlis*, "purpose". It means talking
55
+ plainly and getting to the point. Pronounced **TAH-khe-les**.
56
+
57
+ ---
58
+
59
+ ### It knows your vocabulary
60
+
61
+ General speech models have never heard of your stack. tacheles has.
62
+
63
+ ![Correcting technical vocabulary](docs/media/vocabulary.gif)
64
+
65
+ `fast API` → `FastAPI`. `red is` → `Redis`. `post gres SQL` → `PostgreSQL`. None of that is
66
+ guesswork. It sends your vocabulary with every request, then corrects what comes back against that
67
+ same list.
68
+
69
+ ### It learns the rest
70
+
71
+ The jargon no model could guess, you teach once. It sticks, permanently, and it is applied
72
+ deterministically, not re-guessed each time.
73
+
74
+ ```
75
+ tacheles fix 'P and PM' pnpm # said once, corrected forever
76
+ tacheles review # confirm what it was unsure about
77
+ ```
78
+
79
+ It also ships with corrections for manglings that no vocabulary hint fixes, because what the model
80
+ heard is nothing like the right word: `P and PM` for `pnpm`, `Griffana` for `Grafana`, `item
81
+ potent` for `idempotent`, `cloud code` for `Claude Code`. Those work on a fresh install, and your
82
+ own always take precedence.
83
+
84
+ Every automatic correction is inspectable, and anything it is not certain about is *suggested*
85
+ rather than silently applied. It never rewrites a real word behind your back.
86
+
87
+ ### It speaks more than one language, mid-sentence
88
+
89
+ ![Three languages in one sentence](docs/media/multilingual.gif)
90
+
91
+ English, German and Russian in a single breath, each kept in its own script, with no setting
92
+ required. Declaring the languages you mix is supported and harmless, but in testing here it made
93
+ no measurable difference: Russian came back in correct Cyrillic even when only English was
94
+ declared.
95
+
96
+ ### It can run entirely on your machine
97
+
98
+ Point it at a local whisper.cpp, faster-whisper or Speaches server and **no audio leaves your
99
+ computer at all**. The vocabulary and learning layers work exactly the same, and they matter
100
+ more, because small local models are worse at rare terms rather than better.
101
+
102
+ ```toml
103
+ [transcription]
104
+ backend = "local"
105
+ ```
106
+
107
+ See [docs/LOCAL.md](docs/LOCAL.md) for setup, and for an honest account of what you trade away.
108
+
109
+ ---
110
+
111
+ ## Why not just use the dictation built into your editor?
112
+
113
+ Because it was not built for this. Dictation bundled into AI tools tends to transcribe
114
+ *everything*: every "um", every false start, every repeated word. You get a wall of text and then
115
+ have to edit it. And none of it knows what `pnpm` is.
116
+
117
+ tacheles takes the opposite position on both counts:
118
+
119
+ | | Built-in dictation | tacheles |
120
+ |---|---|---|
121
+ | Filler words | Transcribed verbatim | Dropped, while keeping how you actually talk |
122
+ | Your jargon | Guessed phonetically | Sent as vocabulary, then corrected against it |
123
+ | Getting it wrong twice | Every time | Teach it once, never again |
124
+ | Mixed languages | Usually one language wins | Each kept in its own script |
125
+ | Where your audio goes | Their servers | OpenAI, or nowhere but your own machine |
126
+
127
+ ## Why this exists
128
+
129
+ The bottleneck in working with coding agents is not typing speed. It is the small, constant
130
+ reluctance to explain something properly.
131
+
132
+ You know the shape of what you want. Writing it out means four paragraphs of context: what the
133
+ code does now, what it should do instead, which edge case bit you last time, what not to touch.
134
+ Every one of those paragraphs has a cost, and when you are tired or in the middle of something
135
+ else you pay it by writing less. You send three lines instead of thirty, the agent guesses at the
136
+ rest, and you spend the afternoon correcting work that would have been right if you had just said
137
+ the whole thing.
138
+
139
+ Speaking it costs almost nothing. So you say the whole thing, first time.
140
+
141
+ That is the actual win, and it is not about words per minute. Building
142
+ [PolySimulator](https://polysimulator.com), a paper-trading platform for Polymarket prediction
143
+ markets, we run a lot of agents at once. The scarce resource is not keystrokes, it is attention:
144
+ holding the architecture in your head, reading what came back, judging whether it is right,
145
+ deciding what happens next. Composing precise English is a tax on exactly that attention. Paying
146
+ it ten times a day leaves less of you for the parts that actually need a person.
147
+
148
+ Which is what the name is about. *Tacheles reden* means saying the thing plainly and getting on
149
+ with it, with no throat-clearing and no working up to it. Say what you want; let the work start.
150
+
151
+ There was one thing standing in the way on Linux. Dictation here is bad. Browser microphone
152
+ buttons are flaky, desktop apps cannot type into other apps on Wayland, and local models either
153
+ want a GPU or mangle every technical term. Every tool that got this right turned out to be
154
+ macOS-only, which I discovered the hard way while looking for a name: the good ones were already
155
+ taken by macOS dictation apps.
156
+
157
+ And general transcription mangles developer speech specifically. It writes "P and PM" for `pnpm`,
158
+ "fast API" for `FastAPI`, "red is" for `Redis`, and turns a colleague's name into something
159
+ unrecognisable. When half your sentences carry identifiers, that is not a small annoyance, because
160
+ fixing the transcript costs more than typing it would have. The friction comes straight back, just
161
+ somewhere else.
162
+
163
+ So tacheles sends your vocabulary with every request, corrects what comes back against that same
164
+ vocabulary, and permanently learns whatever you tell it once. The point is that dictating stays
165
+ cheaper than typing all the way through, including the proofreading.
166
+
167
+ It is ~3,700 lines of Python that shells out to tools you already have (`pw-record`, `wl-copy`,
168
+ `notify-send`) and uses OpenAI, or a local Whisper server, for the hard part.
169
+
170
+ *Tacheles*, from the Yiddish **תכלית** *takhlis*, "purpose", is the German idiom for talking
171
+ plainly and getting to the point. *Tacheles reden.* Pronounced **TAH-khe-les**.
172
+
173
+ ## Install
174
+
175
+ ```bash
176
+ git clone https://github.com/ErikEremenko/tacheles && cd tacheles
177
+ ./install.sh
178
+ ```
179
+
180
+ `install.sh` creates a private virtualenv, installs tacheles into it, and registers the GNOME
181
+ shortcuts. Then give it a key and check the setup:
182
+
183
+ ```bash
184
+ tacheles auth login # stored in the GNOME keyring, or a 0600 file
185
+ tacheles doctor # explains anything that will not work, and why
186
+ ```
187
+
188
+ Your key never leaves your machine except in requests to OpenAI. Nobody involved in this project
189
+ receives it, and [docs/CREDENTIALS.md](docs/CREDENTIALS.md) shows you how to verify that rather
190
+ than take it on trust. It also covers using a secret manager (Bitwarden Secrets Manager, pass,
191
+ 1Password, gopass) instead of storing the key at all.
192
+
193
+ `tacheles doctor` is the first thing to run whenever something misbehaves. It reports your
194
+ recorder, credentials, model, delivery method, keyboard layout and hotkeys in one screen.
195
+
196
+ ## How the text reaches your app
197
+
198
+ This is the genuinely hard part on Wayland, which deliberately stops one app synthesising input
199
+ into another. tacheles has three modes, and **always writes the clipboard first** so a failure
200
+ downstream can never lose your transcript.
201
+
202
+ | Mode | What it does | Setup | Notes |
203
+ |---|---|---|---|
204
+ | `clipboard` *(default)* | Copies the text; you press Ctrl+V | none | Always works |
205
+ | `paste` | Copies, then synthesises Ctrl+V | `tacheles setup-paste` | Needs ydotool on GNOME |
206
+ | `type` | Types the text keystroke by keystroke | `tacheles setup-paste` | **US layout only**, see below |
207
+
208
+ **A warning about `type` mode.** ydotool emits raw US keycodes and ignores your active XKB
209
+ layout. If you have a non-US layout configured, typed text comes out mangled. tacheles detects this
210
+ and refuses rather than producing garbage. `paste` mode is unaffected, because only the Ctrl+V
211
+ chord travels through the keyboard, and the text itself goes via the clipboard.
212
+
213
+ ## Seeing what it is doing
214
+
215
+ GNOME hides notification banners after a few seconds whatever expire-timeout you ask for, so a
216
+ notification cannot tell you a recording is still running. The optional top-bar indicator can:
217
+
218
+ ```bash
219
+ tacheles install-indicator # then log out and back in
220
+ ```
221
+
222
+ It shows the state in the panel: microphone when idle, a red dot and a running timer while
223
+ recording, an amber icon while transcribing, a green tick when the text is ready. Clicking it
224
+ opens your recent transcripts; clicking one copies it again, and a middle click on the icon
225
+ copies the newest without opening anything.
226
+
227
+ That list matters more than it sounds. `wl-copy` holds the clipboard in a child process, so a
228
+ session restart empties the clipboard even though the transcript was produced correctly. Nothing
229
+ is ever lost, because it is in the history either way:
230
+
231
+ ```bash
232
+ tacheles last --copy # put the most recent transcript back on the clipboard
233
+ tacheles last -n 5 # or look at the last five
234
+ ```
235
+
236
+ Audio cues are on by default. `tacheles sounds` plays each one, and they are configurable
237
+ under `[notify]`.
238
+
239
+ ## Vocabulary profiles
240
+
241
+ Profiles live in `~/.config/tacheles/profiles/*.toml` and shadow the bundled ones.
242
+
243
+ ```toml
244
+ name = "coding"
245
+ language = "en"
246
+ prompt = """
247
+ Technical software-development dictation. Keep technical terms in canonical casing.
248
+ """
249
+ keywords = ["pnpm", "FastAPI", "PostgreSQL", "Grafana", "idempotent"]
250
+ ```
251
+
252
+ `inherits = "coding"` merges another profile's keywords, so a profile of your own project's names
253
+ can extend the general technical vocabulary rather than repeat it.
254
+
255
+ ```bash
256
+ tacheles profiles # list them
257
+ tacheles toggle -p coding # use one for a single dictation
258
+ ```
259
+
260
+ ## Safety and cost
261
+
262
+ - **Auto-stop.** A forgotten recording stops itself after `audio.max_seconds` (default 5 minutes)
263
+ and transcribes what it captured. Set `max_seconds_action = "discard"` to spend nothing
264
+ instead, or `max_seconds = 0` to disable.
265
+ - **Silence guard.** An all-silent recording is never uploaded, so a muted mic costs nothing.
266
+ - **Spend tracking.** `tacheles history --totals` shows minutes dictated and estimated cost.
267
+
268
+ ## Commands
269
+
270
+ ```
271
+ tacheles toggle [-p PROFILE] start, or stop and transcribe
272
+ tacheles cancel discard the current recording
273
+ tacheles status is it recording?
274
+ tacheles transcribe FILE transcribe an existing audio file
275
+ tacheles doctor check everything
276
+ tacheles models transcription models your key can use
277
+ tacheles history [--totals] recent transcripts and spend
278
+ tacheles config edit edit the config
279
+ tacheles install-hotkeys register GNOME shortcuts
280
+ tacheles uninstall-hotkeys remove them and restore GNOME's originals
281
+ ```
282
+
283
+ ## Hotkeys and your input-source switcher
284
+
285
+ GNOME ships `Super+Space` bound to *switch input source*. Rather than leaving you without a
286
+ layout switcher, `tacheles install-hotkeys` moves that binding to the Windows-style **Shift+Alt**
287
+ and records the original. `tacheles uninstall-hotkeys` puts it back exactly as it was.
288
+
289
+ Shortcuts are appended to your existing custom shortcuts, never overwritten.
290
+
291
+ ## Support
292
+
293
+ The part that does the work is plain Linux. Recording goes through `pw-record`, `parecord` or
294
+ `arecord`, so PipeWire, PulseAudio and ALSA are all covered; the clipboard through `wl-copy`,
295
+ `xclip` or `xsel`, so Wayland and X11 both are; notifications through `notify-send`, which is a
296
+ freedesktop standard. Nothing in the recording, transcription or correction path needs GNOME, and
297
+ that is checked rather than assumed: the core runs in an environment stripped of D-Bus, `DISPLAY`
298
+ and `gsettings`.
299
+
300
+ What GNOME adds is convenience, and each piece degrades on its own when it is absent.
301
+
302
+ | | Status |
303
+ |---|---|
304
+ | Recording, transcription, vocabulary, corrections, clipboard | Works anywhere. Verified with no GNOME present |
305
+ | GNOME / Wayland | Everything, including hotkey installation and the top-bar indicator. Used daily by the author |
306
+ | GNOME / X11 | Should work; `xclip` and `xdotool` cover clipboard and pasting. Not tested |
307
+ | KDE, sway, Hyprland, others | Core works; bind the commands yourself, see [docs/HOTKEYS.md](docs/HOTKEYS.md). Not tested |
308
+ | Automatic paste-chord selection | GNOME only, because the extension is what can see the focused window. Falls back to the configured chord elsewhere |
309
+
310
+ "Not tested" means exactly that. It is expected to work and nobody has confirmed it. If you run
311
+ one of those, an issue saying so either way is genuinely useful.
312
+
313
+ ## Configuration
314
+
315
+ `tacheles config init` writes a fully commented file with every default. Highlights:
316
+
317
+ ```toml
318
+ [general]
319
+ profile = "coding" # default vocabulary profile
320
+ output_mode = "auto" # auto | clipboard | paste | type
321
+
322
+ [openai]
323
+ model = "gpt-transcribe" # $0.0045/min; falls back if unavailable to your key
324
+
325
+ [audio]
326
+ max_seconds = 300 # auto-stop safety limit
327
+ silence_rms = 30 # below this, do not bother uploading
328
+
329
+ [postprocess]
330
+ enabled = false # optional second pass to tidy punctuation
331
+ ```
332
+
333
+ ## How this was built
334
+
335
+ Written by [Erik Eremenko](https://github.com/ErikEremenko), largely with
336
+ [Claude Code](https://claude.com/claude-code) driving the keyboard. I am stating that plainly
337
+ because it is true and because you can see it in the commit history anyway. A project about
338
+ dictating to AI agents that hid its own use of them would be a bit ridiculous.
339
+
340
+ What that means for you as a user or contributor: every design decision here was tested against
341
+ this machine rather than assumed, and the awkward findings are documented rather than smoothed
342
+ over: the ~100 ms clipboard wait, the refusal to type on non-US layouts, the German compound word
343
+ that silently inverted meaning. Where something is unverified, it says so.
344
+
345
+ If you find something that was clearly never exercised, open an issue. That is the failure mode
346
+ worth watching for, and I would rather hear about it.
347
+
348
+ ## License
349
+
350
+ MIT. See [LICENSE](LICENSE). Contributions welcome; see [CONTRIBUTING.md](CONTRIBUTING.md).