Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

quillm — a magic quill transcribing a person speaking at their laptop into a terminal

Neovim whisper.cpp Local Platforms

Stop typing your prompts. Say them.

Press a key, talk, press it again — your words land on the clipboard, ready to paste into Claude Code, Aider, a commit message, anywhere.


Why

Prompting an AI assistant well means writing a lot of prose, and you can speak about four times faster than you can type. quillm turns Neovim into a push-to-talk transcriber: it records your microphone, transcribes the audio with whisper.cpp entirely on your machine, and puts the text on your clipboard.

  • 🎙️ One key<leader>q to start, <leader>q to finish
  • 🔒 Private — no API keys, no cloud, nothing leaves your machine
  • Fast — Metal on Apple Silicon, a second or two on any modern CPU
  • 🪶 Tiny — a few hundred lines of Lua, zero plugin dependencies

Quick start

With lazy.nvim:

{
  "ibramsterdam/quillm.nvim",
  build = "scripts/install.sh",
  opts = {},
}

That's the whole setup. The build step provisions whisper-cli (via Homebrew when available, otherwise a one-time source build) and downloads the speech model. Skipped the build step? quillm heals itself: the model auto-downloads on first use, and :Quillm install runs the same provisioning any time.

Then: hit <leader>q, say "Hey Claude, refactor the payment flow and add specs", hit <leader>q again, and paste.

Verify everything with :checkhealth quillm.

Requirements

What Linux macOS
Recorder (auto-detected) arecord, pw-record, ffmpeg, or sox — virtually every desktop has one ffmpeg or sox (brew install ffmpeg)
Speech-to-text whisper-cli — provisioned by the build step same, via brew install whisper-cpp
Clipboard xclip (X11) or wl-clipboard (Wayland) built in

Neovim ≥ 0.10. For the source-build fallback you need cmake and a C compiler; with Homebrew or a preinstalled whisper-cli, you don't.

On macOS, the first recording triggers the system microphone permission prompt for your terminal app — grant it once.

How it works

 🎙  microphone ──▶ recorder (16 kHz wav) ──▶ whisper.cpp ──▶ "+ register ──▶ 📋 paste
     <leader>q          arecord/ffmpeg…          local STT        clipboard

No daemon, no background service. Recording spawns one process; stopping it hands the WAV to whisper-cli; the result lands in a register. Everything is async — Neovim never blocks.

Commands & keys

Mapping / command Action
<leader>q or :Quillm Toggle: start recording / stop + transcribe + copy
:Quillm cancel Stop recording, discard the audio
:Quillm install (Re)provision whisper-cli and the model
:checkhealth quillm Diagnose recorder, transcriber, model, clipboard

Configuration

Defaults shown — pass only what you want to change:

require("quillm").setup({
  keymap = "<leader>q",   -- toggle key; false to map it yourself
  register = "+",         -- where the transcription lands ("+" = system clipboard)

  -- whisper model; auto-downloaded here on first use
  model = vim.fn.stdpath("data") .. "/quillm/ggml-base.en.bin",

  language = "en",        -- spoken language ("nl", "de", …) or "auto"; non-English needs a multilingual model
  translate = false,      -- translate the transcription to English (whisper's only target)

  record_cmd = nil,       -- function(wav) -> argv, overrides recorder auto-detection
  transcribe_cmd = nil,   -- function(wav) -> argv, replaces whisper-cli entirely
  notify = true,          -- progress notifications via vim.notify
})

Recipes

Another language. The default base.en model is English-only. Grab a multilingual model (no .en in the name) from huggingface.co/ggerganov/whisper.cpp, point at it, and set the language:

opts = {
  model = vim.fn.stdpath("data") .. "/quillm/ggml-small.bin",
  language = "nl",   -- or "auto" to detect per recording
}

Speak Dutch, get English. Whisper translates any language to English locally — add translate:

opts = {
  model = vim.fn.stdpath("data") .. "/quillm/ggml-small.bin",
  language = "nl",
  translate = true,
}

Better accuracy. small.en mishears noticeably less than base.en for a modest speed cost — same setup, different file.

A specific microphone. Override the recorder:

record_cmd = function(wav)
  return { "ffmpeg", "-loglevel", "quiet", "-f", "pulse",
           "-i", "alsa_input.usb-your-mic", "-ar", "16000", "-ac", "1", wav }
end,

A different STT engine. Anything that prints a transcript to stdout works:

transcribe_cmd = function(wav)
  return { "my-stt", "--wav", wav }
end,

Recording indicator in your statusline (lualine shown):

sections = {
  lualine_x = {
    function() return require("quillm").status() end,  -- "● REC" while live
  },
},

Troubleshooting

  • "Heard nothing" — whisper got silence. Check the right mic is the system default, or pin one with record_cmd.
  • Garbled technical terms — whisper guesses at jargon and file paths. Upgrade to small.en, or paste and fix the odd word; it's still faster than typing.
  • Wrong device on macOS — list inputs with ffmpeg -f avfoundation -list_devices true -i "" and pin the index via record_cmd (-i ":1").
  • Everything else: :checkhealth quillm names the missing piece and how to get it.

The name

quill + LLM. A quill that writes down what you say, for the machine you're talking to.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages