Imported from wispr-flow-linux/wispr-flow-linux (
AGENTS.md). Install upstream withnpx skills add wispr-flow-linux/wispr-flow-linux. Copyright stays with the author.
AGENTS.md
Required reading
These documents are the source of truth. If anything in this file conflicts with them, they win. Read them before opening a non-trivial issue or PR.
docs/reference/ipc-contract.md— the IPC contract the clean-room helper implements: command surface, wire framing, message shapes, keycodes (with companionkeycodes.json/commands.json). The technical source of truth for helper behaviour.CONTRIBUTING.md— what we accept, what goes upstream to Wispr, the bash + Rust style requirements, the AI-attribution policy.docs/index.md— entry point for the operator/contributor docs (building, configuration, troubleshooting, decisions, learnings).docs/styleguides/bash_styleguide.md— shell conventions (forked from YSAP). Tabs, 80 cols,[[ ]], noset -e.SECURITY.md— vulnerability reporting; what's in scope vs. what belongs to Wispr.
This file is a fast reference for the highest-leverage rules and the project's accumulated conventions. New policy goes in the style guides or CONTRIBUTING.md.
Project overview
This is an unofficial Linux port of the proprietary Wispr Flow voice-dictation app (an Electron 42 / electron-forge app shipped as a Squirrel Windows installer). It is two things:
- A repackaging pipeline (
scripts/,build.sh) that extracts the app, patches its bundle for Linux, rebuilds native modules, stages a Linux Electron, and produces.deb/.rpm/ AppImage packages. - A clean-room Rust helper that reimplements the one native capability Wispr
Flow ships only for macOS (Swift) and Windows (C#): injecting transcribed text
into the focused application. It is built from the documented IPC contract
(
docs/reference/) and contains no Wispr Flow code. It now lives in its own repo (wispr-flow-linux/helper); see the Repo layout below.
Repo layout
The project spans two repositories under the wispr-flow-linux org:
wispr-flow-linux/wispr-flow-linux(this repo) — the public-domain build scripts and the local packaging makers.wispr-flow-linux/helper— the clean-room Rust helper. It was extracted from this repo and no longer lives here as a local source tree. The helper is consumed as a prebuilt binary pinned inhelper-version.txtand staged via theHELPER_BINenv var (build-linux.sh resolves it).
The hosted distribution layer — the
gh-pagesAPT/DNF tree, thev*tag Releases, the publish/heartbeat workflows, and awispr-flow-linux/workerCloudflare Worker frontingpkg.wispr-flow-linux.dev— is maintainer-run and fails closed with no human gate (D-011). SeeRELEASING.mdand the "Pull request policy" inCONTRIBUTING.mdbefore touching it.
This repo's tree:
build.sh— top-level orchestrator: dispatches the staging pipeline and the per-format packaging makers (--build deb|rpm|appimage,--clean,--doctor).scripts/setup/— host detection, dependency install, Wispr Flow / Electron download helpers.installer-pin.shis the pinned upstream installer (version, URL, sha256): builds download exactly that and verify it; onlycheck-wispr-version.ymlresolves upstream'slatest.json(resolve-installer-url.sh) and rewrites the pin (write-installer-pin.sh).patches/— the app patches:helper-resolver.sh(adds the'linux'helper-path branch),mac-gates.sh(gates the macOS Applications-folder guard to darwin), and the V8 14.8better-sqlite3-multiple-cipherscompat patch.verify-patches.shstatic-greps the repacked bundle for the markers.packaging/—deb.sh,rpm.sh,appimage.shmakers; shared signature<maker>.sh <dist_dir> <version> <arch>.launcher-common.sh— the runtime/usr/bin/wispr-flowlauncher library.doctor.sh— thewispr-flow --doctordiagnostic surface.build-linux.sh— the Phase-0 staging pipeline (see safety rules below).
helper-version.txt— the pinned helper release tag fetched from the helper repo and staged viaHELPER_BIN.docs/reference/— the documented stdin/fd-3 IPC protocol (ipc-contract.mdkeycodes.json/commands.json).
tests/— bats unit tests, per-format artifact tests, and the manual VM-matrix validators.docs/— building / configuration / troubleshooting / decisions / learnings / style guides.nix/,flake.nix— Nix packaging..github/workflows/— CI gates (shellcheck,codespell,test-flags,tests) that run on every push/PR, plus the tag-driven release/publish pipeline (ci.ymlbuild→test→release→APT→DNF→AUR, reusablebuild-amd64/build-arm64/test-artifacts,check-wispr-version,apt-repo-heartbeat,cleanup-runs,update-flake-lock). The publish chain runs on av*tag push; seeRELEASING.md. The worker lives in its own repo (wispr-flow-linux/worker).
Code style
Bash
All shell scripts follow the Bash Style Guide:
- Tabs for indentation, lines under 80 chars (exception: URLs and regex).
[[ ]]for conditionals,$(...)for substitution.- Single quotes for literals, double quotes for expansions.
- Lowercase variables; UPPERCASE only for constants/exports.
localin functions. Noset -e(it interacts badly with$(...)capture and function returns — check status explicitly:cmd || handle_err). Noeval. No POSIX[ ... ]. No backticks.
Lint with shellcheck (and actionlint for workflows) before pushing. Fix the
underlying issue; a per-line # shellcheck disable=SCXXXX with a why-comment is
the last resort.
Rust
The helper's code and its cargo fmt / cargo clippy --all-targets -- -D warnings / cargo test gates live in its own repo (wispr-flow-linux/helper).
This repo consumes the prebuilt binary pinned in helper-version.txt.
Docs / CHANGELOG
- One declarative sentence then a code block or list at the top of every doc page — no "In this guide we will…" preamble.
- Lowercase kebab-case filenames in
docs/. Order lives indocs/index.md. - Troubleshooting headings are the literal symptom, not editorialized prose.
CHANGELOG.mdfollows Keep a Changelog 1.1.0: bullets under Added/Fixed/Changed/etc.
Learnings
The docs/learnings/ directory holds hard-won
technical knowledge from building the port — things not obvious from the code
alone. Consult the relevant entry before working on a subsystem; add a new entry
when you discover something non-obvious that would save the next contributor
(human or AI) significant time.
kwin-zbus-tokio.md— the async-zbus-on- tokio dispatch deadlock that left KDE active-app empty, and the fix.gnome-shell-extension.md— install, relogin requirement, MRU focus fallback, the Introspect pivot.electron42-v8-sqlite.md— the V8 14.8 ABI patch that letsbetter-sqlite3-multiple-cipherscompile.ispackaged-rename.md— why anelectron-named launcher silently breaks DB migrations ("no such table").wayland-injection.md— in-process/dev/uinputvirtual keyboard +ext-data-controlclipboard.global-key-monitor.md— push-to-talk and the shortcut recorder are fed by helperKeypressEvents (XInput2 on X11, evdev/dev/inputon Wayland); the app has no hotkey detection of its own.helper-spawn-env.md— the app spawns the helper with a replacement env (noprocess.env), starving it ofWAYLAND_DISPLAY/DISPLAYso injection silently falls to the no-opstubwhile recording/shortcuts keep working;helper-env.shrestores the env.platform-gates.md— the darwin/win32 carve-outs Linux falls through: the.linux-matches-no-CSS-rule bug behind the shifted side menu, the three gate-shape rules (isMac?:is usually fine,isWindows?:lands Linux on mac defaults,if(win32){}no-else drops functionality), and beautifying the minified bundle withprettier --ignore-path /dev/nullto re-audit a new Wispr version.patching-minified-js.md— the rules for writing patches that survive re-minification:[\w$]+(not\w) for identifiers, anchor on developer strings over churning names, assert the match count, inject a marker for idempotency, and verify against shipped bytes (not a beautified copy). Read before touchingscripts/patches/.test-methodology.md— how a green shell test earns its colour:runsubshells away counter mutations, anchors need a near-miss fixture, one FAIL branch must hit the real tool,[PASS]only on parsed data, and the mutation check (revert the fix, watch a test go red). Read before adding or reviewing a bats test.
Cross-VM testing memory
Operational knowledge from manual VM validation (e.g. issue #32) lives in
.claude/memory/ — committed so it carries across test
VMs and survives agent session resets (only .claude/worktrees/ is gitignored).
Read it before doing VM validation work, and append new hard-won learnings there
(launch quirks, keybind/window/mic gotchas, per-issue validation status). It
complements docs/learnings/ (code-level) with environment/runtime test
knowledge.
GitHub / CI workflow
- Use the
ghCLI for GitHub interactions. - Branch off issue numbers:
fix/123-descriptionorfeature/123-description. - Reference issues in commits/PRs with
#123orFixes #123. - CI gates (
.github/workflows/ci.yml): shellcheck, codespell, flag-parsing, and bats, run on every push/PR. On av*tag, the same workflow runs the build→test→release→APT/DNF/AUR publish chain. SeeRELEASING.md. - Claude Code hooks (
.claude/settings.json):.claude/hooks/pre-pr-lint.shruns the CI shellcheck line, codespell over tracked files, actionlint on changed workflows and (when a shell or bats file changed)bats tests/*.batsbefore anygit push, and blocks the push on a failure;.claude/hooks/session-start.shinstalls the missing tools at session start with a passwordless sudo, or says which are missing.
Issues, PRs, and commits
Keep them short and plain. An issue says what is wrong and what should be true instead, in a few sentences. A PR says what changed and why this way, enough for a reviewer to decide where to look. The evidence (test counts, command output, the reasoning that ruled out the obvious approach) goes in the commit body, where someone deep-diving is already reading, and the diff is the authority over both. No headers, no checklists, no bullet retelling of the diff, no em-dashes. Say what was deliberately left out.
Reference the issue with Fixes #123 (or Refs #123). A commit subject
states what changed; the body states why, with the evidence. Credit reused or
cherry-picked contributor work by handle, in the body.
AI-assisted work discloses with one trailer line at the end of a PR or issue body, using the actual model name:
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Commits carry Co-Authored-By: Claude <claude@anthropic.com>. Comments carry
no trailer.
