Shell notification sound on selected exit codes; interactive installer for zsh/bash/fzf.
Project description
faah
Play a short sound when your interactive shell hits “command not found” (127) or “not executable” (126) by default — not for every failing command (false, grep miss, etc.), unless you opt in. Optional fzf defaults and Cursor / VS Code helper file copies.
Install (PyPI / uv) — recommended
pip install faah
# or
uv tool install faah
Then run the interactive installer (requires a TTY):
faah
# same as:
faah install
Use faah help for usage (preferred over faah --help; both work).
Roadmap
Released changes are listed in CHANGELOG.md. Planned direction for 2.1+ and later (plugin-style hooks for visuals and audio, optional config file, shell parity, CI/portability) is in ROADMAP.md.
This syncs bundled shell assets into ~/.config/faah/ (or $XDG_CONFIG_HOME/faah when XDG_CONFIG_HOME is set) and adds a single marked block to ~/.zshrc and/or ~/.bashrc that sources the managed init/faah.{zsh,bash} files.
Non-interactive (e.g. CI):
faah install --yes
Terminal-matrix (cmatrix-style F / A / H / ! rain) is the only visual: it runs on faah usage mistakes and on shell-hook triggers (127 / 126 by default), unless you opt out. faah install asks about matrix for each shell you install; faah install --yes defaults to on. Opt out with --no-matrix (adds export FAHH_DISABLE_MATRIX=1 in a matrix-disable block). Older installs may still have banner-env / matrix-env blocks — a new faah install removes those legacy markers.
Other commands:
| Command | Purpose |
|---|---|
faah help |
Show CLI usage (preferred over faah --help) |
faah doctor |
Check mpv/ffplay/paplay/aplay, fzf, sound file |
faah doctor --fix |
On Debian/Ubuntu with apt-get, install missing tools (sudo) |
faah play |
Play the sound once |
faah terminal-matrix |
F / A / H / ! rain: full-screen on a TTY; scrolling line flood when stderr is not a TTY (use -s / --seconds for duration) |
faah uninstall |
Remove faah blocks from rc files and install/ helpers |
faah --version |
Package version |
Upgrade:
pip install -U faah
# or
uv tool upgrade faah
If you previously installed 2.0.0rc1, upgrade with pip install -U faah (or uv tool upgrade faah) to get the current line. 2.0.1 includes the mpv no-window fix for faah play / play-faah.sh. 2.1.0 (terminal-matrix overhaul, hook fixes, and related changes) and 2.1.1 (longer default matrix durations) are documented in CHANGELOG.md.
Development (from this repo)
Uses uv (see pyproject.toml and uv.lock).
uv sync --all-extras
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run pytest -q
uv run ruff check src tests
uv run python -m build
Console entrypoint: faah → faah.cli:main.
Repository layout
Everything user-facing after install lives under ~/.config/faah/, populated from a single bundled tree in the Python package:
src/faah/
├── __init__.py # version
├── cli.py # Typer CLI
├── cli_exit.py # usage-error (exit 2) → matrix
├── terminal_matrix.py # cmatrix-style effect + plain flood
├── sound.py # play MP3 via mpv/ffplay/…
├── installer/ # rc blocks, sync bundled data, editor copies
└── data/ # only copy of shell assets (sdist/wheel)
├── assets/sounds/fahhh.mp3
├── scripts/play-faah.sh
├── zsh/ bash/ fzf/ init/
├── cursor/ # optional editor fragments (→ install/cursor when selected)
└── vscode/
tests/ # pytest
.github/workflows/ # CI + PyPI publish
| Path | Role |
|---|---|
| src/faah/ | Python package; src/faah/data/ is the bundled shell + sound + editor snippets |
| src/faah/data/cursor/README.md | Cursor terminal notes |
| src/faah/data/vscode/README.md | VS Code terminal notes |
| ROADMAP.md | Planned features and design direction (2.1+) |
Environment variables
| Variable | Meaning |
|---|---|
XDG_CONFIG_HOME |
If set, managed config is $XDG_CONFIG_HOME/faah (must not point at a parent of your clone such that this path equals the repository root). If unset, faah uses ~/.config/faah (i.e. $HOME/.config/faah). |
FAHH_ROOT |
Managed config root (default: ~/.config/faah when using installed snippets) |
FAHH_SOUND |
Path to sound file (default: $FAHH_ROOT/assets/sounds/fahhh.mp3) |
FAHH_DISABLED |
If non-empty, hooks do nothing |
FAHH_PLAY_ON_NONZERO |
If set and FAHH_PLAY_EXIT_CODES is unset: play on any non-zero exit |
FAHH_PLAY_EXIT_CODES |
Space-separated codes that trigger sound (default: 127 126). Use all for any non-zero. |
FAHH_IGNORE_EXIT |
When mode is all: codes to never trigger sound (default: 130) |
FAHH_DISABLE_MATRIX |
If 1 / true / yes / on, skip terminal-matrix visuals (CLI usage errors and shell hooks). Set by faah install --no-matrix. |
FAHH_REPLACE_NOT_FOUND |
If set (non-empty, not 0/false/no/off), install command_not_found_handler (zsh) or command_not_found_handle (bash 4+). Unknown commands: matrix + sound only — no default command not found line. |
FAHH_MATRIX_SEC |
Duration for faah terminal-matrix when -s is omitted (default ~1.7 s). |
FAHH_MATRIX_CLI_SEC |
Duration for faah usage mistakes (exit 2); default ~1.2 s (faster than a full run). |
FAHH_MATRIX_HOOK_SEC |
Duration when zsh/bash hooks invoke matrix (default ~1.44 s). |
FAHH_PYTHON |
Interpreter for python -m faah when faah is not on PATH (default python3). Must be a Python that has faah installed. |
FAHH_MATRIX_FPS |
Frames per second (8–60, default ~26). |
FAHH_MATRIX_CHARS |
Character set (default FAH!). |
Troubleshooting
-
Usage errors / hooks: On
faah …mistakes (exit 2),faah.cli:mainruns terminal-matrix on stderr (unlessFAHH_DISABLE_MATRIX). For unknown shell commands, setFAHH_REPLACE_NOT_FOUND=1so zsh/bash call faah’s handler (matrix + sound) instead of the defaultcommand not foundline. Runfaah install --yesafter upgrading so~/.config/faah/zsh/faah.zsh/bash/faah.bashstay current. -
Not a TTY: You still get a scrolling F/A/H/! flood (lighter than before). Hooks try
faahfirst, thenpython3 -m faahif the CLI is not onPATH.faah doctorshows whetherfaahwas found. -
Feels slow: Lower
FAHH_MATRIX_SEC,FAHH_MATRIX_HOOK_SEC, orFAHH_MATRIX_CLI_SEC. RaiseFAHH_MATRIX_FPS(e.g. 32) for snappier TTY animation. -
No module named faahafter a bad command: the shell hook used to runpython3 -m faahwhenever thefaahprogram was missing fromPATH. System/usr/bin/python3often has no faah installed — that message is from Python. Fix: install the CLI (pip install faah,uv tool install faah, oruv syncin the repo and putfaahonPATH), or runfaah install --yesafter upgrading so~/.config/faahmatches. The hook now runspython3 -m faahonly ifimport faahsucceeds (silent otherwise). Optional:export FAHH_PYTHON=/path/to/venv/bin/pythonif faah is only installed in a venv. -
No sound: Install mpv or ffplay; run
faah doctor. -
A window opens when playing (mpv/ffplay): faah passes
--force-window=no,--no-video, and--vo=nullto mpv, and-nodispto ffplay. Runfaah install --yesto refresh~/.config/faah/scripts/play-faah.sh, or upgrade faah. If it persists, checkmpv --versionand your desktop session (Wayland/X11). -
faah installruns the hook instead of the CLI (e.g. you seefaah:source:and the Python CLI never runs): Older rc snippets defined a shell function namedfaahthat shadowed the realfaahon yourPATH. Workaround: runcommand faah install --yesonce (zsh/bash: bypasses the function; same idea as\faahin zsh). After that,faah installupdates the marked block to a one-liner that does not definefaah, so the CLI works normally. -
source: no such file ... ~/.config/faah/init/faah.zsh: Your rc still runs the faah bootstrap, but~/.config/faahis missing or incomplete (removed by hand, failed install, etc.). Fix: runfaah install --yes(orcommand faah install --yesif the function still shadows—see above), or remove the faah block from~/.zshrc/~/.bashrc(seefaah uninstall). To edit rc without loading it:zsh -fthennano ~/.zshrc. The managed line is[[ -r ... ]] && source ...so a missing top-level init file does not error on startup. -
Other errors from faah on startup: The rc line only skips loading when that main
init/faah.{zsh,bash}is absent or not readable. It does not silence failures fromsourceitself (e.g. syntax error in that file) or from nestedsourcelines inside managed files (e.g. a partial/corrupt tree). Those messages are intentional so you notice a broken install—runfaah install --yesorfaah doctor, or remove the faah block. -
assets/,bash/,zsh/, … appeared at the root of your git clone (withXDG_CONFIG_HOMEunset): The intended location is~/.config/faah, not the repo. This usually means~/.config/faahwas a symlink to your clone (orXDG_CONFIG_HOMEused to be wrong so$XDG_CONFIG_HOME/faahwas the clone). Fix: remove the symlink and use a real directory (mkdir -p ~/.config/faahafter removing the link), delete stray copies from the clone (git status, then remove untrackedassets/,bash/, … only if they are not part ofsrc/faah/data/), runfaah doctor— it reports unsafe if the resolved path still looks like a source checkout. Newer faah versions refuse to sync into a path that looks like the development repository. -
Editors: Integrated terminals must load your interactive shell rc. See src/faah/data/cursor/README.md and src/faah/data/vscode/README.md.
Contributing
See CONTRIBUTING.md.
Pre-commit (optional)
pre-commit install
pre-commit run -a
License
Scripts and documentation: MIT License (SPDX-License-Identifier: MIT).
The bundled sound may be subject to separate rights; replace it with your own file if needed.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file faah-2.1.1.tar.gz.
File metadata
- Download URL: faah-2.1.1.tar.gz
- Upload date:
- Size: 62.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
25446251e8f7c24fbb46204d3cc71e7dfe6a13a4d79508056050e0992caac890
|
|
| MD5 |
8e0cb8a9612cf14fac0d22c96ed863aa
|
|
| BLAKE2b-256 |
d0c6932f32b54bb2c1efa694534182e7953cab724e2c176c2853b26956202417
|
Provenance
The following attestation bundles were made for faah-2.1.1.tar.gz:
Publisher:
publish.yml on guilyx/faah
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
faah-2.1.1.tar.gz -
Subject digest:
25446251e8f7c24fbb46204d3cc71e7dfe6a13a4d79508056050e0992caac890 - Sigstore transparency entry: 1271443854
- Sigstore integration time:
-
Permalink:
guilyx/faah@6b27d7eef79462be53f6629bc30ce9686c11b565 -
Branch / Tag:
refs/tags/v2.1.1 - Owner: https://github.com/guilyx
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6b27d7eef79462be53f6629bc30ce9686c11b565 -
Trigger Event:
release
-
Statement type:
File details
Details for the file faah-2.1.1-py3-none-any.whl.
File metadata
- Download URL: faah-2.1.1-py3-none-any.whl
- Upload date:
- Size: 65.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
40ff036ff5ee4a29f1b14961ff65971bb194eaa4a3f6cab75c09ca226fbd66ba
|
|
| MD5 |
61893610870cf8712811178c034684af
|
|
| BLAKE2b-256 |
29f8e393e47dc612402dede4695c176a573dbf39df5837d5dea432b2624386de
|
Provenance
The following attestation bundles were made for faah-2.1.1-py3-none-any.whl:
Publisher:
publish.yml on guilyx/faah
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
faah-2.1.1-py3-none-any.whl -
Subject digest:
40ff036ff5ee4a29f1b14961ff65971bb194eaa4a3f6cab75c09ca226fbd66ba - Sigstore transparency entry: 1271443926
- Sigstore integration time:
-
Permalink:
guilyx/faah@6b27d7eef79462be53f6629bc30ce9686c11b565 -
Branch / Tag:
refs/tags/v2.1.1 - Owner: https://github.com/guilyx
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6b27d7eef79462be53f6629bc30ce9686c11b565 -
Trigger Event:
release
-
Statement type: