# AGENT-INSTALL.md — installing VZT Flow with an AI agent
This file is written for an **AI coding agent with shell access** (Claude
Code, or anything comparable) that has been pointed at this repo and asked to
install [VZT Flow](README.md) for its human. It is a runbook, not prose: run
the steps in order, verify with the stated command, and stop where it says to
stop.
> **Agent: read this file verbatim, not a summary of it.** If you reached it
> through a fetch tool that summarizes pages, the flags and stop conditions
> below get compressed away — and a summarized read of this file has, in
> testing, invented steps that aren't here. Pull it down and read the bytes:
>
> ```bash
> curl -fsSL https://raw.githubusercontent.com/vonzelle-vzt/vzt-flow/main/AGENT-INSTALL.md -o /tmp/vzt-agent-install.md
> ```
Humans: you don't need this file. Use the
[one-liner](README.md#macos-one-liner). If you'd rather have your agent do it —
Claude Code, Codex CLI, Gemini CLI, or anything comparable with shell access —
paste this into it:
> Install VZT Flow on this machine by following
> https://raw.githubusercontent.com/vonzelle-vzt/vzt-flow/main/AGENT-INSTALL.md
---
## 0. Preflight — read before running anything
**What gets installed, and where.** Nothing here is a package manager; the
installer writes to exactly these paths:
| Path | What |
|---|---|
| `/Applications/VZT Flow.app` | The menu-bar app (macOS) |
| `/usr/local/bin/flow`, else `~/.local/bin/flow` | The `flow` CLI |
| `~/.vzt-flow/mcp/` | The MCP server (`index.js` + `node_modules`) |
| `~/.config/vzt-flow/models/` | Parakeet ASR (~640 MB), optional cleanup LLM (~1.1 GB) |
| `~/.claude.json` (via `claude mcp add --scope user`) | MCP registration |
**Network + time budget.** The release assets are ~40 MB. Models are the
expensive part: Parakeet is a 456 MB download, the optional cleanup LLM
another ~1.1 GB. On a slow link the model step can exceed a default shell-tool
timeout — see [Step 2](#2-download-the-models).
**Platform gate.** Check `uname -s` / `uname -m` first and tell your human
what they're getting:
| Platform | Reality | Installer | Installs |
|---|---|---|---|
| macOS Apple Silicon | Supported, tested | `scripts/install.sh` | app + CLI + MCP |
| macOS Intel | CI-built, CPU-only inference, never run on real Intel hardware | `scripts/install.sh` | app + CLI + MCP |
| Linux x86_64 | Experimental; CI-built, never run on real Linux hardware. X11 full, Wayland degraded. No cleanup LLM, no meeting mode | `scripts/install.sh` | app + CLI + MCP |
| Windows x64 | Experimental, but **verified end to end on real Windows hardware (2026-07-10)**. No cleanup LLM, no per-app profiles. Installers are unsigned — SmartScreen warns | `scripts/install.ps1` | app + CLI + MCP |
**Windows ships the CLI and MCP server as of v0.3.0.** `install.ps1` installs
`flow.exe`, adds it to the user PATH, registers the MCP server, and can download
the model, so Steps 2 and 4 apply there too. It remains untested on real Windows
hardware — say so rather than promising it works.
Anything else (Windows on Arm, 32-bit, BSD): stop and say so. Don't improvise a
build from source unless asked.
**Consent.** This writes to `/Applications`, may invoke `sudo` (Linux `.deb`
path only), downloads ~1.6 GB if models are included, and modifies the user's
`claude mcp` configuration. Confirm with your human before Step 1 unless they
already told you to go ahead. If they asked for "just the app, no models," pass
`INSTALL_MODELS=none`.
**What you cannot do.** macOS permission grants (Microphone, Accessibility,
Input Monitoring) are TCC-protected: they cannot be granted from the shell, by
`sudo`, or by editing a plist. Step 3 is a human step. Do not attempt to
automate it, and do not report the install as complete before it happens — the
hotkey does not work without it.
---
## 1. Install the app, CLI, and MCP server
macOS / Linux:
```bash
curl -fsSL https://raw.githubusercontent.com/vonzelle-vzt/vzt-flow/main/scripts/install.sh \
| INSTALL_YES=1 NO_LAUNCH=1 INSTALL_MODELS=none bash
```
Windows (PowerShell):
```powershell
iwr https://raw.githubusercontent.com/vonzelle-vzt/vzt-flow/main/scripts/install.ps1 -UseBasicParsing | iex
```
The flags exist for you specifically:
| Flag | Why an agent wants it |
|---|---|
| `INSTALL_YES=1` | Skips the "overwrite existing app?" prompt. Without it the installer blocks on `read` and your shell call hangs. |
| `NO_LAUNCH=1` | Doesn't `open` the app. Launch it in Step 3, once, at the moment the user is looking at the screen to answer permission dialogs. |
| `INSTALL_MODELS=none\|asr\|all` | Model download. Left at `none` here on purpose — see Step 2. |
| `NO_APP=1` | Installs only the CLI, MCP server and models; never touches `/Applications`. Use it when the app came from Homebrew, and only then. |
| `GITHUB_TOKEN` | Only if unauthenticated GitHub API calls are rate-limited (60/hr per IP). Not normally needed; the repo is public. |
The installer auto-registers the MCP server with **Claude Code only**, if the
`claude` CLI is on PATH (and only if `node` is also on PATH — see
[Step 0.5](#05-node-is-required-for-the-mcp-server-not-for-flow-itself)). For
any agent, the MCP server itself is a plain stdio server — only the
*registration* step differs. The node entry path is the same for every agent:
`$HOME/.vzt-flow/mcp/index.js`.
| Agent | Registration |
|---|---|
| **Claude Code** | `claude mcp add vzt-flow --scope user -- node "$HOME/.vzt-flow/mcp/index.js"` (done automatically by Step 1 if `claude` and `node` are both on PATH) |
| **Codex CLI** | Add to `~/.codex/config.toml`:
`[mcp_servers.vzt-flow]`
`command = "node"`
`args = ["/.vzt-flow/mcp/index.js"]` |
| **Gemini CLI** | Add to `~/.gemini/settings.json`, under an `mcpServers` object:
`{ "mcpServers": { "vzt-flow": { "command": "node", "args": ["/.vzt-flow/mcp/index.js"] } } }` |
Resolve `$HOME` to an absolute path yourself before writing the TOML/JSON —
neither format expands shell variables.
Sources checked for the Codex/Gemini syntax above (fetched directly, not
recalled from memory): Codex CLI MCP config —
(redirects to
); Gemini CLI MCP
config — . Both formats can
churn between releases — if `claude mcp add` / `codex mcp list` / `gemini mcp
list` (below) shows the server as registered but not connected, re-check
these docs rather than assuming the snippet above is still current.
### 0.5. node is required for the MCP server, not for `flow` itself
`flow` (the CLI and the app) is a standalone Rust binary — no node needed.
The MCP server is a compiled Node/TypeScript stdio server and does need node
(>=18; `@modelcontextprotocol/sdk`'s declared minimum). `scripts/install.sh`
checks for `node` before registering the MCP server: missing or too old, it
still installs the app + CLI and skips MCP registration with a warning
instead of writing a registration that fails at runtime. If you see "MCP
server: skipped — node not found" in the installer's summary, install node
() and re-run the registration command for your agent
from the table above.
**Already installed via Homebrew?** `brew install --cask vonzelle-vzt/vzt/vzt-flow`
installs the `.app` only. Run the script afterward with **`NO_APP=1`** to add the
CLI and MCP server. That flag is not optional here: without it the script
`rm -rf`s `/Applications/VZT Flow.app` and installs its own copy, leaving `brew`
with a receipt for a bundle it no longer wrote.
---
## 2. Download the models
**Windows:** `install.ps1` already downloads the model unless you passed
`-InstallModels none`. To do it by hand, use `flow.exe` in place of `flow` below.
Parakeet (speech-to-text) is **required**; nothing transcribes without it. The
cleanup LLM is **optional** — it powers `clean`/`polish` modes, and `raw`/`code`
modes never touch it. macOS only for now; Windows and Linux have no cleanup LLM.
Run these as separate commands rather than folding them into Step 1's
`INSTALL_MODELS`, because a 456 MB (or 1.6 GB) download will blow past the
default timeout on most agent shell tools and you'll lose the process:
```bash
flow models download parakeet-v3 # required — 456 MB down, ~640 MB on disk
flow models download cleanup # optional — ~1.1 GB, macOS only
```
Give each one a **generous explicit timeout** (10 minutes) or run it
backgrounded and poll. Both are idempotent and resumable-by-re-running: a
failed or interrupted download is fixed by running the same command again, and
`--force` re-downloads a model you suspect is corrupt.
If `flow` isn't found, the CLI landed in `~/.local/bin` and that directory isn't
on PATH. Call it by absolute path rather than editing the user's shell profile
without asking.
`INSTALL_MODELS=asr` (Parakeet) or `INSTALL_MODELS=all` (both) does the same
work inline during Step 1 — correct for CI or an unattended box, wrong for an
interactive agent session that will time out.
---
## 3. Permissions — hand this to your human
Confirm the app actually landed before launching it — if Step 1 failed, `open -a`
reports `Unable to find application named "VZT Flow"`, which reads like a
permissions problem and isn't one:
```bash
test -d "/Applications/VZT Flow.app" && open -a "VZT Flow" || echo "app missing — Step 1 did not complete"
```
macOS will prompt for permissions as they're first needed. If the prompts were
dismissed, these open the exact panes:
```bash
open "x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone"
open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"
open "x-apple.systempreferences:com.apple.preference.security?Privacy_ListenEvent"
```
Three grants, all required, none optional:
1. **Microphone** — records the audio.
2. **Accessibility** — synthesizes the paste keystroke, reads the focused field
for paste verification.
3. **Input Monitoring** — the `CGEventTap` that watches for the Right Option
hotkey.
Tell your human, verbatim, what to do: *open System Settings → Privacy &
Security, enable VZT Flow under each of those three, and quit-and-relaunch the
app afterward.* Toggling a grant does not apply to an already-running process.
On **Linux** there is only a microphone grant; there is no accessibility grant
to make. On **Windows** there are none of these.
---
## 4. Verify — and don't skip this
**Windows:** this step applies — use `flow.exe doctor` / `flow.exe transcribe`.
The hotkey is Ctrl+Shift+Space there, and it has never been tested on real
Windows hardware, so ask your human to confirm rather than asserting it works.
`flow doctor` is the oracle. It reports every piece of state this install
touches, so read its output rather than assuming:
```bash
flow doctor
```
A healthy macOS install prints, among other lines:
```
flow-cli version: 0.2.0
Parakeet v3 model: PRESENT
Default input device: MacBook Air Microphone (48000 Hz, 1 channel(s))
Cleanup model: PRESENT
Daemon socket: PRESENT and alive (/Users/you/.config/vzt-flow/daemon.sock)
MCP registration: vzt-flow IS registered with `claude mcp`
```
`Parakeet v3 model: MISSING` means Step 2 didn't finish. `Daemon socket` absent
means the app isn't running — fine if you passed `NO_LAUNCH=1` and haven't
opened it yet, a problem otherwise. Note `flow doctor`'s `MCP registration`
line currently only checks `claude mcp` specifically — if you're running as
Codex CLI or Gemini CLI, its `NOT registered` reading doesn't mean your
agent's own registration (checked below) is missing; this is a known gap in
`flow doctor`'s current wording, not yours to work around.
Then prove the transcription pipeline actually works, end to end, on real
audio. This needs no microphone, no permissions, and no network — `say` and
`afconvert` ship with macOS:
```bash
say -o /tmp/vzt-check.aiff "the quick brown fox jumps over the lazy dog"
afconvert -f WAVE -d LEI16@16000 -c 1 /tmp/vzt-check.aiff /tmp/vzt-check.wav
flow transcribe /tmp/vzt-check.wav
```
Expected — the sentence back, at a realtime factor around 0.1–0.2x on Apple
Silicon:
```
Transcription wall time: 0.558s | audio: 2.92s | realtime factor: 0.191x
Segments:
[0.00s - 2.84s] The quick brown fox jumps over the lazy dog.
```
If that transcript is right, the models and the ASR engine are good. Note what
it does **not** cover: microphone capture, the global hotkey, and the paste
step all depend on Step 3's grants and can only be verified by a human holding
Right Option and talking. Ask them to, then report.
Last, confirm the MCP server is reachable — the check differs per agent:
| Agent | Verify |
|---|---|
| **Claude Code** | `claude mcp list` — expect `vzt-flow ... ✔ Connected` |
| **Codex CLI** | `codex mcp list` — expect `vzt-flow` with a healthy status (add `--json` for a scriptable check) |
| **Gemini CLI** | `gemini mcp list` — expect `vzt-flow` as `Connected` (stdio servers show `Disconnected` in an untrusted folder — run `gemini trust` first if so) |
A fresh session of your agent is required before the `listen` /
`transcribe_file` / `dictation_history` / `meeting_transcript` tools appear.
---
## 5. Report honestly
Tell your human exactly this shape of thing, with the parts that are true:
- Installed: app at `/Applications`, CLI at ``, MCP registered.
- Models: Parakeet present; cleanup LLM present / skipped.
- Verified: `flow doctor` clean, TTS round trip transcribed correctly.
- **Not** verified by me: mic capture, hotkey, paste — needs the three
permission grants and a human saying a sentence.
- Next: hold Right Option, talk, release. Transcript pastes at the cursor.
Don't claim the hotkey works. You have no way to know.
---
## Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Installer hangs with no output | `read -p` overwrite prompt; you forgot `INSTALL_YES=1` | Kill it, re-run with the flag |
| `no release asset matching '*.dmg' found` | GitHub API rate limit (60/hr per IP, unauthenticated) | Set `GITHUB_TOKEN`, or install `gh` — the script prefers `gh release download` |
| `flow: command not found` | CLI went to `~/.local/bin`, not on PATH | Call it by absolute path; suggest the `export PATH=` line, don't edit their profile unprompted |
| `Parakeet v3 model: MISSING` | Step 2 skipped or interrupted | Re-run `flow models download parakeet-v3` — idempotent |
| Hotkey does nothing, app is running | Input Monitoring / Accessibility not granted | Step 3, then **quit and relaunch** the app |
| Hotkey stopped working right after a rebuild, or after upgrading from v0.3.0 or earlier | Those builds were ad-hoc signed; macOS pinned the grants to the old binary's hash, so they no longer match. The checkbox still looks ticked. | `tccutil reset Accessibility com.vzt.flow` and `tccutil reset ListenEvent com.vzt.flow`, then re-grant. Released builds from v0.3.1 on are Developer ID signed and keep their grants across upgrades. |
| `clean`/`polish` produce raw text | Cleanup model missing, or generation missed the 2500 ms deadline | `flow models download cleanup`; the raw-on-deadline fallback is by design |
| MCP tools absent in Claude Code | Registered after the session started | Restart `claude`; check `claude mcp list` |
| Transcript on clipboard, "paste may have failed" | Secure or unreadable focused field | Press Cmd+V. Expected in password fields and some Electron apps |
Deeper: [docs/USAGE-macOS.md](docs/USAGE-macOS.md) ·
[docs/USAGE-Windows.md](docs/USAGE-Windows.md) ·
[docs/USAGE-Linux.md](docs/USAGE-Linux.md) ·
[docs/MEETINGS.md](docs/MEETINGS.md)
---
## Uninstall
macOS and Linux. (Windows: uninstall "VZT Flow" from Settings → Apps.)
```bash
claude mcp remove vzt-flow --scope user
rm -rf "/Applications/VZT Flow.app" ~/.vzt-flow
rm -f /usr/local/bin/flow ~/.local/bin/flow
rm -rf ~/.config/vzt-flow # config, history, and the ~1.7 GB of models
```
Confirm the last line with your human before running it — it deletes their
config, dictionary, snippets, and dictation history along with the models.
Leave the revoked permission entries in System Settings; macOS prunes them.