Notifications

Native macOS banners — they respect Focus and Do Not Disturb like anything else. There are two kinds, both configured in Settings… → Notifications.

Session alerts

Fire when a session wants you, for terminal and IDE sessions alike. Two independent toggles:

  • Waiting for your input — a session is blocked on a question or a plan review.
  • Task finished — a session ended a turn and handed control back.

Both fire on every matching event while enabled. There is no “only if it took longer than N minutes” gate; the only suppression is a short per-session rate limit so a chatty session can’t spam you. Banners group per session in Notification Center.

The banner subtext is the project folder name — the last component of the session’s working directory. The title already carries the message, so the hook’s own text isn’t repeated.

Click a banner to focus the session

  • IDE sessions — the workspace window (VS Code, Cursor, Windsurf, JetBrains), opened through LaunchServices so no security prompt appears.
  • CLI sessions — the terminal app, detected generically as the GUI app that owns the session. Works with Terminal, iTerm2, Warp, Ghostty, WezTerm and anything else; there is no hardcoded list.
  • Codex sessions in the ChatGPT desktop app — the app itself, activated without a folder since it has no window bound to one.

A session that can’t be resolved is a silent no-op rather than an error.

Usage alerts

Fire when session-window or weekly utilization crosses a threshold — 80% by default, and the list is editable. Each threshold fires once per crossing: it stays silent while you remain above the line and re-arms only when utilization drops back below it. The armed state survives relaunches, and this polling runs even when the menu bar percentage is hidden.

How session alerts actually work

Two entry points, deliberately non-overlapping, so each event fires exactly once.

Terminal (cli) sessions use a Claude Code hook. BlackFlare installs Notification and Stop hooks in ~/.claude/settings.json pointing at a small bundled blackflare-hook forwarder. Claude Code runs it at ask-time and turn-end, so the banner fires the instant Claude blocks — even when the transcript write is still buffered on disk, which is exactly the case a transcript-only watch misses for terminals.

IDE sessions use the transcript watch. BlackFlare watches ~/.claude/projects/*/*.jsonl with kernel-level FSEvents, reads only the newly appended bytes, and distinguishes an unanswered AskUserQuestion or ExitPlanMode (waiting) from a turn-ending end_turn that ran long enough (task done). Every transcript is seeked to end-of-file at launch, so starting BlackFlare never replays your history as a burst of banners.

The FSEvents path emits nothing for cli sessions — that is the hook’s job — so a terminal session can never produce a duplicate banner.

“Task finished” is rate limited to at most one per session per 30 seconds on both paths. Everything stays on your machine: the hook relay carries only the event, message, session ID and working directory; the transcript watch reads locally. No content leaves your Mac.

One deliberate simplification: permission-pending is not its own banner. Every Notification event reads as “waiting”.

Codex notifications

Codex needs no hook. BlackFlare tails ~/.codex/sessions/**/rollout-*.jsonl and fires “Codex finished a task” on each task_complete — covering CLI, VS Code, and ChatGPT desktop app sessions, since they all write to the same rollout tree.

Codex has no waiting signal. Approval prompts never reach the rollout file, so every turn end is reported as task-finished and nothing is ever guessed into “waiting”. Because Codex reports the same source for the VS Code extension and the desktop app, BlackFlare tells the host apart by the rollout’s originator.

Delivery is gated by the Codex provider toggle plus the shared “Task finished” toggle — symmetric with Claude, with no separate Codex alert switch.

What BlackFlare writes to settings.json

Only its own Notification and Stop entries, identified by the blackflare-hook command. Your other hooks and every other byte are untouched, and an install → uninstall round-trip is byte-identical. Turning a session toggle off removes the matching hook. Moving the app repairs the stale helper path on the next launch. Changes apply to your next Claude Code session.