@senad-d/micme
Local voice dictation for Pi coding prompts. Press Alt+M, speak, review, send.
Package details
Install @senad-d/micme from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@senad-d/micme- Package
@senad-d/micme- Version
0.3.6- Published
- Jul 16, 2026
- Downloads
- 1,906/mo · 70/wk
- Author
- senad-d
- License
- MIT
- Types
- extension
- Size
- 1.3 MB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./src/extension.ts"
],
"image": "https://raw.githubusercontent.com/senad-d/micme/main/img/demo.gif"
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
MicMe is a Pi extension for coding prompts. It records your microphone with ffmpeg, transcribes locally with whisper.cpp or another local backend, and inserts the transcript into pi.
- Local-first: no telemetry and no cloud STT service by default.
- Review-first: transcripts paste into the editor unless you enable auto-submit.
- Pi-native: install globally, project-locally, from git, or from a source checkout.
- Configurable: use
/micme conf,~/.pi/agent/micme.json, or shell variables.
Security: pi packages run with your full system permissions. Micme can use your microphone, spawn local commands, write
MICME_*settings to~/.pi/agent/micme.json, and optionally download Whisper model files. ReadSECURITY.md.
Table of Contents
- Quick Start
- Installation
- Pi Compatibility
- Backend Setup
- Configuration
- Commands
- Models and Backends
- Troubleshooting
- Diagnostics
- Update and Uninstall
- Development
- Publishing
- License
Quick Start
pi install npm:@senad-d/micme
Install a local backend. On macOS:
brew install ffmpeg whisper-cpp
Start pi and configure Micme:
pi
/micme conf
/micme devices
Use it:
- Press
alt+m/Option+mto start recording. - Speak your prompt.
- Press
alt+magain to stop. - Review the pasted transcript and press Enter.
Micme is toggle-based. Press-and-hold recording is not used because terminal key-up events are not portable. A toggle received while Micme is starting, stopping, transcribing, or finalizing is rejected with a warning; wait for that operation to finish before toggling again.
Installation
| Scope | Command | Notes |
|---|---|---|
| Global | pi install npm:@senad-d/micme |
Loads in every trusted pi project. |
| Project-local | pi install npm:@senad-d/micme -l |
Writes to .pi/settings.json in the current project. |
| One run | pi -e npm:@senad-d/micme |
Try without changing settings. |
| Git | pi install git:github.com/senad-d/micme@<tag> |
Pin a tag or commit. |
| Local checkout | pi -e . |
Develop or test this repository. |
Source checkout:
git clone https://github.com/senad-d/micme.git
cd micme
npm install --ignore-scripts
npm run doctor
pi -e .
Use the checkout globally while developing:
pi install /absolute/path/to/micme
Pi Compatibility
Micme supports @earendil-works/pi-coding-agent and @earendil-works/pi-tui versions >=0.80.7 <0.81.0. CI installs and tests both packages at 0.80.7, typechecks every production TypeScript file against their real declarations, and loads the default extension through Pi's jiti-based loader. Supporting a new Pi minor release requires intentionally updating the peer ranges, pinned development versions, and contract gate together.
Backend Setup
Micme does not bundle recorder, transcriber, or model binaries.
| OS | Install | Device setting |
|---|---|---|
| macOS | brew install ffmpeg whisper-cpp |
Run /micme devices, then set MICME_AUDIO_DEVICE=<audio id> or MICME_AVFOUNDATION_INPUT=:<audio id>. |
| Linux | Install ffmpeg and whisper.cpp with your package manager, Nix, Homebrew, or source build. |
MICME_PULSE_SOURCE=default |
| Windows | winget install Gyan.FFmpeg, then install/build whisper.cpp. |
MICME_DSHOW_AUDIO_DEVICE=Microphone Name |
List devices inside pi:
/micme devices
macOS device listing outside pi:
ffmpeg -hide_banner -f avfoundation -list_devices true -i ""
If whisper-cli is not on PATH, set it explicitly:
MICME_WHISPER_CPP_BIN=/path/to/whisper-cli
MICME_WHISPER_CPP_MODEL=/path/to/ggml-small.en.bin
Configuration
MicMe reads settings from shell environment variables and from the global Micme config file at ~/.pi/agent/micme.json. Shell variables win. /micme conf writes only MICME_* keys to that JSON file, so settings follow you across pi projects on the same machine. If this file is malformed or its top level is not an object, Micme refuses recording, model download, device discovery, and transcription before starting external work; /micme conf remains available so you can locate and fix the file.
Config saves are serialized in-process and across Pi processes with micme.json.lock, then atomically replace the file with mode 0600. Each save re-reads the latest file while holding the lock and changes only its requested MICME_* keys, preserving $schema and other metadata. Saves commit in lock-acquisition order, so the last successful save to the same key wins. Micme waits up to five seconds for the lock; on timeout it leaves the existing config and lock untouched because it cannot safely assume another writer's lock is stale. Remove a leftover lock directory manually only after verifying that no Pi process is saving Micme settings.
Numeric runtime settings use bounded fallbacks after integer normalization. Invalid, non-finite, or out-of-range values never reach timers or command arguments: transcription timeouts allow 1000..3600000 ms; stream step/length/keep/flush values allow 100..60000, 1000..120000, 0..60000, and 100..60000 ms; stream tokens allow 1..4096; committed word chunks allow 1..10; capture ids allow -1..255; VAD allows 0.01..0.99; and audio sample rates allow 8000..384000 Hz. See the schema for each fallback default.
The usual setup path is to run:
/micme conf
You can also edit the JSON manually. Minimal ~/.pi/agent/micme.json:
{
"$schema": "https://raw.githubusercontent.com/senad-d/micme/main/micme.schema.json",
"MICME_LANGUAGE": "en",
"MICME_TRANSLATE_TO_ENGLISH": "off",
"MICME_TRANSCRIBE_BACKEND": "auto",
"MICME_AUTO_DOWNLOAD_MODEL": "1",
"MICME_DEFAULT_WHISPER_CPP_MODEL": "small.en",
"MICME_AUTO_SUBMIT": "0",
"MICME_KEEP_AUDIO": "0"
}
For temporary one-off overrides, set shell variables when starting pi, for example MICME_KEEP_AUDIO=1 pi.
Common settings:
| Variable | Meaning |
|---|---|
MICME_TRANSCRIBE_BACKEND=auto |
Backend selector. auto preserves priority: custom command, whisper.cpp, then Python Whisper. |
MICME_TRANSCRIPTION_MODE=clip |
Stable default: transcribe after recording stops. |
MICME_TRANSCRIPTION_MODE=stream |
Experimental append-only live dictation with whisper-stream; requires whisper.cpp. |
MICME_LANGUAGE=en |
Transcription language when translation is off. Use auto for detection. |
MICME_TRANSLATE_TO_ENGLISH=off |
“Translate from” option. Leave off, or set the spoken source language code such as hr, bs, de, or tr to output English. |
MICME_STREAM_KEEP_CONTEXT=0 |
Stream default: avoid Whisper prompt carry-over so live chunks are less likely to rewrite each other. |
MICME_STREAM_FLUSH_MS=700 |
Runtime-default quiet interval before tentative words are committed append-only. Selecting the stream profile in /micme conf writes 650. |
MICME_STREAM_FINALIZE_WITH_CLIP=0 |
Keep the append-only live transcript on stop. Set 1 to opt in to final clip replacement. |
MICME_AUTO_SUBMIT=0 |
Paste for review. Set 1 to send automatically. |
MICME_SHORTCUT=alt+m |
Toggle shortcut. Use terminal syntax (alt+m, ctrl+space, f8) or a printable character such as §. Restart or /reload after changing. |
MICME_VALIDATE_AUDIO=1 |
Reject near-silent recordings and fail closed when ffmpeg omits or truncates the required max_volume metric. With a custom recorder and no ffmpeg, Micme warns and skips this guard; set 0 to disable it intentionally. |
MICME_RECORD_SAMPLE_RATE=auto |
Raw recording sample-rate override. Leave auto so ffmpeg uses the selected input's native rate. |
MICME_RECORD_SYNC=1 |
Automatically preserve wall-clock recording duration from ffmpeg timestamps. |
MICME_RECORD_METER=0 |
Quality-safe default: meter from raw.wav instead of a second ffmpeg stdout branch. Set 1 to restore the legacy live meter pipe. |
MICME_AVFOUNDATION_DROP_LATE_FRAMES=0 |
macOS only: ask AVFoundation not to drop late frames when capture lags. |
MICME_KEEP_AUDIO=0 |
Delete successful recording audio. Set 1 to keep each recording under ./micme-rec/rec-###/ for debugging. |
MICME_MODEL_DIR=~/.cache/whisper.cpp |
Model cache/discovery directory. |
See micme.example.json for the full JSON template.
Streaming mode treats whisper-stream output as repeated, overlapping hypotheses rather than committed word deltas. Micme only appends committed words to the editor during live streaming; tentative words are held internally and committed after agreement, overlap, stop, or the quiet interval configured by MICME_STREAM_FLUSH_MS. If the editor already contains text, Micme separates the existing text from dictated words with a space when needed. If MICME_STREAM_FINALIZE_WITH_CLIP=1, the stop-time clip transcript can intentionally replace the live append-only text.
Commands
| Command | Description |
|---|---|
/micme |
Toggle recording. |
/micme conf |
Open the TUI configuration screen. |
/micme devices |
Show compact audio/video capture device inventory. |
/micme last |
Paste the previous transcript again. |
/micme audio |
Show the last kept audio directory. |
/micme help |
Show short help. |
Models and Backends
Micme supports these backend values in JSON/env config:
whisper.cpp: useswhisper-cli/whisper-cppand a ggml/gguf model path.python: uses the OpenAI WhisperwhisperCLI and a Python model name.
/micme conf keeps the interactive backend picker focused on whisper.cpp and Python Whisper, then shows only the model/binary fields for the selected backend. For whisper.cpp, MICME_WHISPER_CPP_MODEL is the selected ggml/gguf model path; if it is unset, Micme falls back to ${MICME_MODEL_DIR}/ggml-${MICME_DEFAULT_WHISPER_CPP_MODEL}.bin. For Python Whisper, MICME_WHISPER_MODEL is passed as the CLI model name.
With MICME_AUTO_DOWNLOAD_MODEL=1, Micme downloads missing standard whisper.cpp models into MICME_MODEL_DIR. Existing model paths must point to regular files; directories are rejected before download or transcription. Header, body, and file-output inactivity is bounded to two minutes; timed-out or cancelled downloads remove their temporary file. Concurrent requests for the same model share one transfer: cancelling one caller leaves it running for other callers, while cancelling the final owner aborts it. Session shutdown and closing /micme conf cancel their ownership and suppress later process starts, saves, notifications, and renders. Disable downloads with:
MICME_AUTO_DOWNLOAD_MODEL=0
Recommended model progression: base.en for speed, small.en for a stronger default, medium.en for accuracy. Translation uses Whisper's built-in translate task and needs translate-capable multilingual models; when MICME_TRANSLATE_TO_ENGLISH is enabled, Micme maps .en model names to multilingual defaults (small.en → small, base.en → base) and maps non-translation turbo models to the closest translate-capable model (large-v3-turbo → large-v3).
Advanced users can replace the recorder or transcriber:
MICME_RECORD_COMMAND=ffmpeg -hide_banner -loglevel error -f avfoundation -i :0 -af aresample=async=1:first_pts=0 -ac 1 -y {audio}
MICME_TRANSCRIBE_COMMAND=whisper-cli -m /path/to/model.bin -f {audio} -otxt -of {tempDirRaw}/out -nt -np && cat {tempDirRaw}/out.txt
{audio}, {tempDir}, and {transcript} are shell-quoted. *Raw placeholders bypass quoting and should only be used when you fully control the command.
Recorder and streaming commands must remain alive until Micme stops them. A requested stop accepts exit code 0, a signal Micme sent during escalation, its conventional shell exit code, or ffmpeg's signal-stop code 255; spawn errors, spontaneous exits, unrelated signals, and other nonzero codes are reported as failures. Partial recorder audio is kept for debugging but is not transcribed. When final-clip streaming is enabled, Micme explicitly falls back to the append-only live transcript if that optional clip recorder fails.
Troubleshooting
| Problem | Try |
|---|---|
| No backend found | Use /micme conf to choose whisper.cpp or Python Whisper, install the selected backend, or configure a trusted custom command manually. |
| Wrong microphone | Run /micme devices, then set the OS-specific device variable. |
| Crackling or shortened audio | Keep MICME_RECORD_SYNC=1 and MICME_RECORD_METER=0; on macOS keep MICME_AVFOUNDATION_DROP_LATE_FRAMES=0, and avoid Aggregate/BlackHole devices unless intentionally routing audio. |
| Unrelated transcript | You probably recorded silence. Set MICME_KEEP_AUDIO=1 and check /micme audio. |
| Slow transcription | Use whisper.cpp, a smaller model, and shorter recordings. |
Option/Alt inserts § or µ |
Set MICME_PRINTABLE_SHORTCUTS=§ or change MICME_SHORTCUT, then /reload. |
| Need automatic sending | Set MICME_AUTO_SUBMIT=1. |
Diagnostics
npx -p @senad-d/micme micme-doctor
From a source checkout:
npm run doctor
The doctor checks Node, pi, ffmpeg, requested/effective backend, effective model, whisper.cpp, optional whisper-stream, model paths, and macOS devices when available. Custom command values are redacted.
For stream state investigation, start pi with MICME_STREAM_DIAGNOSTICS=1. Diagnostics are opt-in and include sanitized frames, extraction mode, committed words, and tentative candidate words.
Update and Uninstall
pi update --extensions # update installed pi packages
pi update npm:@senad-d/micme # update Micme only
pi remove npm:@senad-d/micme # remove global install
pi remove npm:@senad-d/micme -l # remove project-local install
Development
npm ci
npm run typecheck
npm run test:pi-contract
npm run lint
npm run format:check
npm run validate
Publishing
Micme publishes to npm as @senad-d/micme. You need an npm account with publish access to the @senad-d scope.
npm login
npm whoami
npm run publish:npm
The publish script asks for the version number, validates the package, runs npm version <version> to update package.json and package-lock.json, creates the v<version> git tag, publishes with npm publish --access public, and then offers to push the release commit and tag.
Run it only from a clean working tree after updating CHANGELOG.md.
License
MIT
