Local meeting transcription tool. Captures audio from any sound device and produces timestamped, speaker-labelled transcripts using whisper-large-v3-turbo on Apple Silicon GPU and pyannote for speaker diarization. Everything runs locally — no audio leaves your machine.
- macOS with Apple Silicon (M1/M2/M3/M4)
- uv
- Python 3.11+
- A free HuggingFace account (for pyannote model access)
1. Install dependencies
uv venv --python 3.11
uv pip install -e .2. Configure tokens
cp .env.example .envEdit .env and fill in:
HUGGINGFACE_TOKEN=hf_... # from https://huggingface.co/settings/tokens
ANTHROPIC_API_KEY=sk-ant-... # only needed for --claude mode
3. Accept HuggingFace model terms
Visit each of these and click "Agree and access repository":
- https://huggingface.co/pyannote/speaker-diarization-3.1
- https://huggingface.co/pyannote/speaker-diarization-community-1
- https://huggingface.co/pyannote/segmentation-3.0
Models (~1.5 GB total) download automatically on first run.
4. Grant microphone permission
Go to System Settings → Privacy & Security → Microphone and enable access for your terminal app. Required to capture mic input.
5. Install the launcher
Run this from the repo directory to create a ~/bin/scribe wrapper:
mkdir -p ~/bin
printf '#!/bin/bash\nexec uv run --directory "%s" scribe-pick "$@"\n' "$(pwd)" > ~/bin/scribe
chmod +x ~/bin/scribeMake sure ~/bin is on your PATH (add export PATH="$HOME/bin:$PATH" to your shell profile if needed). Then anywhere on your machine:
scribeYour system already exposes virtual audio devices you can use directly:
| Device | Captures |
|---|---|
| Microsoft Teams Audio | Teams call audio |
| ZoomAudioDevice | Zoom call audio |
Run
scribe devicesto see current device indices — they can shift when devices are added/removed.
For other apps (Slack, Meet, etc.) install BlackHole, then in Audio MIDI Setup:
- Create a Multi-Output Device: BlackHole 2ch + your headphones
- Create an Aggregate Device: BlackHole 2ch + your mic
- Set the app's speaker output to the Multi-Output Device
- Use the Aggregate Device as the scribe input — captures both sides of the call
scribeShows a live device picker — all input devices with real-time RMS metering so you can see which one has signal:
Select audio input device
────────────────────────────────────────────────────────────
▶ [ 5] Scribe (Yeti + Teams Audio) (44100 Hz, 3 ch) ███░░ -24 dB
[12] Yeti Stereo Microphone (44100 Hz, 2 ch) █░░░░ -42 dB
[14] Microsoft Teams Audio (16000 Hz, 1 ch) ░░░░░ ---
────────────────────────────────────────────────────────────
↑↓ navigate Enter start q quit
Press Enter to begin live transcription. Transcripts are saved automatically to transcripts/ with a datetime + content-derived filename. Press Ctrl-C to stop.
Any flags you pass are forwarded to the live session:
scribe --no-diarize # skip speaker labels (faster)
scribe --speakers 2 # hint: number of speakers
scribe --save-wav out.wav # also save raw audioSkip the picker and start directly with a known device index:
uv run scribe live --device 9Options:
| Flag | Default | Description |
|---|---|---|
--device / -d |
required | Input device index |
--output / -o |
auto | Output markdown file |
--chunk |
10 |
Chunk size in seconds |
--save-wav |
— | Also save raw WAV recording |
--no-diarize |
off | Skip speaker labels (faster) |
--speakers |
auto | Hint: number of speakers |
--language / -l |
auto | Language code, e.g. en |
uv run scribe devicesCapture to a WAV file for later processing. Press Ctrl-C to stop.
uv run scribe record --device 9 --output recording.wavProcess a saved WAV — full-file diarization is more accurate than live chunked mode.
uv run scribe transcribe recording.wav
uv run scribe transcribe recording.wav --output transcripts/meeting.md --speakers 3Send the transcript to Claude for cleanup, a summary, and action items. Requires ANTHROPIC_API_KEY in .env.
uv run scribe transcribe recording.wav --claudeClaude appends a Summary & Action Items section to the markdown output.
Transcripts are saved as markdown:
# Transcript
_Started: 2026-05-07 10:55_
> **[00:00] SPEAKER_00:** Hey, can everyone hear me?
> **[00:04] SPEAKER_01:** Yep, loud and clear.
> **[00:07] SPEAKER_00:** Great, let's get started.Re-running against an existing file appends with a --- Resumed: timestamp separator rather than overwriting.
Live transcription — just run scribe, pick your device, talk:
scribeRecord now, transcribe later — lower latency during the call, better diarization accuracy after:
# During the call
uv run scribe record --device 9 --output recording.wav
# After the call — full diarization + Claude summary
uv run scribe transcribe recording.wav --claudeLive + save WAV — transcribe in real-time and keep the raw audio:
scribe --save-wav recording.wav- Silence is skipped — chunks below an energy threshold are not sent to Whisper, preventing hallucinations on silent audio.
- Speaker IDs are consistent across chunks in live mode but may not match between separate runs.
- First run is slow — models download once and are cached in
~/.cache/huggingface. - Privacy — all processing is local. Nothing is sent to any server unless you use
--claude.