TL;DR: faf-cli now fires native desktop notifications for the moments that matter — Trophy unlocks at 100%, tier upgrades, and long-running command completion. Inspired by Ghostty's notification model. Silent on terminals that don't support it. Opt out with one env var.

The Moment That Started It

You're running faf auto on a real project. The scan takes 15 seconds. You glance at Slack, answer a quick message, look up — was that done already? Did it work? You scroll back through the terminal to find the score.

This release fixes that. When faf auto finishes a long scan, your OS notification center pops up with the result. When you cross 100%, you get a 🏆 Trophy notification regardless of how long the run took. When a tier upgrade happens (Bronze → Silver, Silver → Gold), you know.

The mental model: one command, one notification, at the moment of payoff.

How It Works

faf-cli emits an OSC 9 escape sequence — a tiny terminal control code that supported terminals forward to the OS notification center. Ghostty does it. iTerm2 does it. Wezterm and Kitty do it. Everywhere else, the sequence is silently ignored. No native bindings, no platform-specific code paths, no extra dependencies.

This is how Claude Code's "waiting for your input" notifications appear in Ghostty. Same mechanism, same elegance. We borrowed the model.

The Three Rules

Notifications are easy to ship and easy to ruin. Three rules guided every decision:

  1. Valuable — only fires when there's something worth interrupting a human for. Not every step. Not progress. Not errors that are already visible in the terminal.
  2. Timely — fires at the moment of value, not before, not after. Quick commands (under 5 seconds) don't notify because you were probably watching.
  3. One-click gone — never repeats for the same event. Dismissal is the OS's job, not ours.

The 5-second duration guard is the key UX choice. It's calibrated for faf-cli's command profile — not the 10-second build-tool norm. faf auto on a small cached project finishes in 1–3 seconds, you see it, no notification needed. On a real project that takes 10–30 seconds, attention has drifted, the notification earns its space.

What Notifies, And When

TriggerWhen
faf auto TrophyAlways — reaching 100% is a celebration, not a duration question
faf auto tier upgradeAlways, only on score improvement (e.g. Silver → Gold)
faf auto long scanIf the run took 5 seconds or more
faf go completionAlways — interactive command, you almost certainly drifted between phases
faf sync completionIf the sync took 5 seconds or more

Click-To-Dismiss On macOS

By default macOS treats OSC 9 notifications as banners — they auto-dismiss in a few seconds. Some users (myself included) prefer the click-to-dismiss model, where notifications stay top-right until you actually look at them.

If terminal-notifier is on your PATH, faf-cli routes through it instead:

brew install terminal-notifier

Then in System Settings → Notifications → terminal-notifier, switch to "Alerts". Every faf-cli notification now stays until you click. Optional — OSC 9 still works without it.

Opt Out

One env var. No config file. No flag.

export FAF_NO_NOTIFY=1

For users who want the OSC 9 path even when terminal-notifier is installed, FAF_NOTIFY_OSC9=1 forces it.

Try It

npm install -g faf-cli@6.3.0

Run faf auto in a real project from Ghostty or iTerm2. Watch your notification center.

The Numbers

  • v6.3.0 — released April 27, 2026
  • 409/409 — tests passing
  • 100% — 🏆 Trophy score on project.faf
  • 10 lines — the entire notify() helper
  • 5 seconds — duration guard, calibrated for faf-cli's command profile
  • Zero new dependencies — OSC 9 is just an escape sequence

Why "Ghostty-Inspired"

Ghostty is the terminal that made me notice the notification model in the first place. Watching Claude Code emit "waiting for your input" pop-ups while I was in another window — that was the moment. Same mechanism, different consumer.

Crediting the inspiration matters. Same pattern as F1-inspired engineering — we borrow what works, name where it came from, and keep building.