ParrotFlow needs Apple silicon and macOS 15 or later. It uses about 3 GB of disk and about 1 GB of memory. For a short version of this page, see Download.
Install with Homebrew
brew install znat/tap/parrotflow
The cask lives in znat/homebrew-tap. It reads the checksum from the published release. It sets auto_updates true, because the app installs its own updates.
The cask also links the binary onto your PATH as parrotflow. Use it for the command line flags, for example parrotflow --check-config. The install script does not add this command. If you used the script, write the full path /Applications/ParrotFlow.app/Contents/MacOS/ParrotFlow wherever this page says parrotflow.
Do not use parrotflow to start the app. With no arguments in a terminal, it prints how to start the app and exits with code 2. A copy started from a shell gets permissions credited to the shell, not to ParrotFlow. Start the app like this:
open -a ParrotFlow
Install with the script
curl -fsSL https://raw.githubusercontent.com/znat/parrotflow/main/scripts/install.sh | sh
The script downloads ParrotFlow.zip from the latest GitHub release, installs ParrotFlow.app in /Applications and starts it. The download is about 3 MB.
If /Applications is not writable, install somewhere else with PARROTFLOW_DEST:
curl -fsSL https://raw.githubusercontent.com/znat/parrotflow/main/scripts/install.sh | PARROTFLOW_DEST=~/Applications sh
If the download fails, check that a release exists on the releases page.
What the script checks
The script and the app’s own updater check a download the same way, in the same order. Nothing is installed until every check passes.
- The published SHA-256 matches the archive.
codesign --verify --deep --strictsays the signature covers the whole bundle.codesign -Rsays the certificate was issued by Apple to the ParrotFlow team.spctl --assess --type executesays Apple notarized it.
You do not need to turn off Gatekeeper, remove the quarantine attribute or use sudo. If something seems to need one of these, something else is wrong.
Check the install
--check-config validates the config and prints what the app will use. It also reports the microphone permission.
parrotflow --check-config
It cannot report Accessibility. macOS credits a check made from a terminal to the terminal, so it reads as missing even when it is granted. The app checks it at launch and writes the result to its log:
grep "launched —" ~/Library/Logs/ParrotFlow.log | tail -1
You want accessibility=Granted. See Permissions.
To check transcription without speaking, make a test clip with say and transcribe it:
say -o /tmp/pf-check.wav --data-format=LEI16@16000 --channels=1 "Testing one two three"
parrotflow --transcribe /tmp/pf-check.wav
The first run downloads the speech model. Any text that comes back means transcription works.
Update
The app checks GitHub for new releases and offers them in a panel with the release notes. You have three answers:
- Update and restart downloads, checks and replaces the app.
- Skip this version never offers that version again.
- Later.
The check sends nothing about you or what you dictate. updates.after_days in config.yaml sets how long a release must exist before it is offered:
updates:
after_days: 7 # 0 offers a release the day it ships (default); -1 never asks
You can also update by running the install command again. It replaces the app in place. This is the way in when the app cannot install over itself.
From a terminal:
parrotflow --update-check [--after-days N]
parrotflow --update-install [--dry-run]
Releases are signed with the same certificate, so an update keeps your Microphone and Accessibility grants.
Upgrading from v0.9.0 or older
Releases up to v0.9.0 were signed with a self-signed certificate. Later releases use a Developer ID, which is a new identity. The updater in those old releases refuses the new build. Run the install command again, or install with Homebrew. You then grant Microphone and Accessibility once more.
Where files live
| What | Where |
|---|---|
| App | /Applications/ParrotFlow.app |
| Config | ~/.config/parrotflow/config.yaml, created on first launch |
| Vocabulary | ~/.config/parrotflow/vocabulary.yaml |
| Your transforms | ~/.config/parrotflow/transforms/<name>/, one folder each |
| Built-in transforms | ~/.config/parrotflow/transforms/built-in/, refreshed from the app on every launch |
| Recordings | ~/.config/parrotflow/recordings, empty unless logging.audio: true |
| Trace | ~/.config/parrotflow/recordings/trace.jsonl |
| Timeline | ~/.config/parrotflow/recordings/spans.jsonl |
| Log | ~/Library/Logs/ParrotFlow.log |
| Python for parsing | ~/Library/Application Support/ParrotFlow/python, about 170 MB |
| API keys | your keychain, under the service ParrotFlow |
Do not edit files in transforms/built-in/. The app replaces them on the next launch. To change a built-in transform, copy its folder into transforms/<name>/ and point command: at the copy.
The app reloads config.yaml and vocabulary.yaml when you save them. No restart is needed.
Set PARROTFLOW_CONFIG_DIR to point the command line flags at another config directory. The app reads it too, so do not export it from your shell profile.
When something goes wrong
Most answers are in the log. Every start records the key and both permissions. Every clip records whether speech was found and what was transcribed.
tail -30 ~/Library/Logs/ParrotFlow.log
If System Settings shows a permission as granted but the app disagrees, see Reset a permission.
--bug-report prints the version, macOS, the chip, the permission state, the --check-config output and the last 50 lines of the log. Home paths are written as ~.
parrotflow --bug-report
The full reference is in setup.md and distribution.md on GitHub.