A standalone, on-device transcription CLI for macOS. quill transcribe <file>
turns a local audio file, or the audio track of an .mp4 or .mov video, into
a markdown transcript.
Transcription happens on your machine, through Core ML. Your audio is not sent anywhere. The only network traffic is downloading a model variant you do not have yet; once it is cached, Quill runs with no network connection at all. Pure audio in, markdown out.
- macOS 13 (Ventura) or later.
- macOS only by design: WhisperKit is Core ML-based.
Building from source, which is how Quill is installed, additionally needs a Swift 6.2 toolchain (Xcode 26). The installed binary does not.
Clone the repository and install a release build:
git clone https://github.com/async-digital-ltd/quill-cli.git
cd quill-cli
make installmake install builds with swift build -c release and copies the binary to
/usr/local/bin/quill, which may need sudo. To install somewhere you own
instead, set PREFIX; the binary goes to $PREFIX/bin/quill:
make install PREFIX="$HOME/.local" # installs to ~/.local/bin/quillThen quill is runnable from any shell that has that directory on its PATH.
Remove it with make uninstall, passing the same PREFIX if you set one.
quill transcribe <file> [--output <path>] [--model <variant>]- The transcript is written to standard output as markdown, or to a file
with
--output <path>(-o). - Progress is written to standard error, so
quill transcribe clip.m4a > out.mdcaptures only the transcript. - Quill reads files through Core Audio. Audio files work (
.m4a,.mp3,.wav,.flac,.aiff, …), and so do.mp4and.movvideos, whose audio track is transcribed. Matroska and WebM (.mkv,.webm) do not open; extract the audio to one of the formats above first.
Examples:
quill transcribe interview.m4a > interview.md
quill transcribe interview.m4a --output interview.md
quill transcribe interview.m4a --model openai_whisper-smallThe first run for a given model downloads it from Hugging Face into
~/Library/Application Support/Quill/models/… and reuses it thereafter, with no
network connection. Two repositories are involved: argmaxinc/whisperkit-coreml
for the Core ML weights, and the matching openai/whisper-* for the tokenizer.
Set QUILL_CACHE_DIR to put that cache somewhere else: another volume, or a
tree you already back up:
export QUILL_CACHE_DIR="$HOME/caches/quill"The model and its tokenizer both land under whichever directory is in effect.
Licences: the Core ML models in argmaxinc/whisperkit-coreml are MIT. They are
converted from OpenAI's Whisper weights, which Hugging Face lists as Apache-2.0
for openai/whisper-tiny through openai/whisper-large-v3 and MIT for
openai/whisper-large-v3-turbo. Downloading from Hugging Face is also subject
to Hugging Face's terms of service.
Pick a model with --model (-m); the trade-off is footprint and speed against
accuracy:
| Variant | Size (approx.) | Notes |
|---|---|---|
openai_whisper-tiny |
~75 MB | Fastest, roughest. Good for quick checks. |
openai_whisper-base |
~145 MB | Default. Balanced; comfortable on 16 GB machines. |
openai_whisper-small |
~500 MB | Noticeably more accurate; still light. |
openai_whisper-large-v3_turbo |
~1.5 GB | Most accurate; heaviest. |
base is the default because it is the one that behaves on a constrained
machine: it stays well within memory on 16 GB and downloads quickly.
It is not the right default for every source. base is more prone to mangling
unfamiliar terms and, less often, to inverting meaning on fast speech. For
terminology-sensitive or archival work (technical talks, product and brand
vocabulary, rapid speakers), pass --model openai_whisper-small or
openai_whisper-large-v3_turbo and accept the memory and time cost.
swift build
swift testEvery dependency is public, so a clean clone builds with no credentials configured. See CONTRIBUTING.md before opening a pull request, and AGENTS.md for the architecture and conventions.
Worth stating plainly, because people transcribe sensitive recordings:
- Your audio never leaves the machine. Transcription runs on-device through Core ML, and Quill never sends your audio or transcripts over the network.
- The network is used only to download a model you do not have yet. Each
run first checks the cache for the variant named by
--model. If a complete copy is there, the run makes no network request of any kind, so it works offline. If not, WhisperKit downloads the Core ML weights fromargmaxinc/whisperkit-coremland, as a separate request, the tokenizer from the matchingopenai/whisper-*repository, and later runs reuse both. Name the variant in full, as in--model openai_whisper-small: a short name such as--model smallis resolved against Hugging Face, so it goes online every time. - A Hugging Face token is sent only with a download. WhisperKit's Hugging
Face client looks for a token in
HF_TOKEN,HUGGING_FACE_HUB_TOKEN,HF_TOKEN_PATH,$HF_HOME/token,~/.cache/huggingface/tokenand~/.huggingface/token, and if it finds one it sends it as anAuthorizationheader on the download requests. The models are public and need no token, so unset it for the first run if you would rather not send it. A run from the cache sends nothing. - No telemetry, no analytics, no crash reporting. There is nothing to opt out of.
- Nothing is retained but downloads. The cache under
~/Library/Application Support/Quillholds model and tokenizer files. During a download, macOS's networking layer also keeps its own HTTP cache of the responses under~/Library/Caches/quill, outsideQUILL_CACHE_DIR. Neither holds audio or transcripts: those go to standard output or to the path you name, and nowhere else.
The command-line surface is documented and covered by semver in
docs/cli-contract.md: invocation, streams, markdown
format, and exit codes. If you are scripting against quill, that document is
the thing to depend on, and changes to it follow semver.
MIT. See LICENSE.