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.
pip install faah
# or
uv tool install faahThen run the interactive installer (requires a TTY):
faah
# same as:
faah installUse faah help for usage (preferred over faah --help; both work).
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 --yesTerminal-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 faahIf 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.
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 buildConsole entrypoint: faah → faah.cli:main.
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+) |
| 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!). |
-
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.
See CONTRIBUTING.md.
pre-commit install
pre-commit run -aScripts 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.