# Ember for Mac

> Ember is a free macOS menu bar app that watches your AI coding agents and tells you, from the menu bar, which one needs you. The icon burns while an agent works, goes idle when it stops, and taps you the moment one is waiting on a permission or a question. The same panel lists the dev servers running inside your repositories, one click to open and one to kill. Built by Stijn Hanegraaf, a product designer in Amsterdam.

## What it does

- One icon in the menu bar with three states: burning while an agent is working, idle when it has stopped, needs you when it is blocked on a permission prompt or a question. The icon shows the most urgent session; the panel shows them all.
- Click the icon for the panel: every live session, its state as a pill (Burning, Idle, Needs you), the repository it is working in, and which agent it is. Sessions are grouped by agent. A header line counts them ("2 burning · 1 idle"); with nothing running it says "No tokens burning".
- Click a session row and Ember brings that terminal tab or app window forward: Terminal.app, iTerm2, Ghostty, the Claude desktop app and the ChatGPT desktop app, found by walking the process tree, not by guessing from a window title. A Codex thread shows its own name on its row.
- Rows are named after their repository, not the directory the agent happens to be in, so a name stays put while an agent moves around. Two sessions in the same repository are told apart by their terminal.
- LOCALHOST: every dev server listening from inside one of your git repositories is listed under the sessions, one row per process, by repository name, with its port and how long it has been up. Click a row to open it in the browser. Hover it for Kill, which sends the server SIGTERM (the same as Ctrl-C). Kill all stops every listed server at once. Ports that do not belong to a repository (system services, music apps) are never listed.
- Alerts, all off by default: a notification banner when an agent needs you; one sound for needs you and a second sound for a finished session, each with a Test button; and no sound at all for a session whose window is already frontmost.
- Six icon styles (Ember, Robot, Breathe, Comet, Bars, Ring) drawn on one grid with one stroke weight, in monochrome (the default) or any colour. The picker shows all six animating at once.
- Settings: show the session count in the menu bar, hide idle sessions, show local servers, notify when an agent needs me, play a sound, open at login, and automatic update checks. Settings opens in its own window with an icon-tab navigation (⌘,).

## Agents it watches

- Claude Code, version 2.1 or newer (hooks in ~/.claude/settings.json).
- Codex, both the CLI and the ChatGPT desktop app (hooks in ~/.codex/hooks.json).
- Gemini CLI (hooks in ~/.gemini/settings.json).
- Copilot CLI (its own file, ~/.copilot/hooks/ember.json).
- Grok Build (its own file, ~/.grok/hooks/ember.json).

All of them land in the same panel with the same three states. Settings lists only the tools that are actually installed on the Mac, with an Install button next to each; Install writes that agent's hooks (backing up the file first) and Remove takes them out again. Cursor is deliberately not supported: its hooks fire for commands it would allow anyway, so a needs-you from it would be wrong.

## How it works

Each agent fires hooks at the interesting moments of a session: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied, Elicitation, Notification (permission prompt), Stop, SessionEnd. Ember installs those hooks once, all pointing at one small helper, ember-hook, which writes one JSON file per session into ~/Library/Application Support/Ember/sessions/. The app watches that folder. That is the whole mechanism: no polling, no network, no Accessibility permission, nothing reading the screen. The helper never prints to the terminal and always exits 0, so a hook that fails can never break the session it reports on.

The three states come from a fixed table: a prompt or a tool call means burning; a permission request, an AskUserQuestion or a permission-prompt notification means needs you; Stop means idle; SessionEnd removes the row.

## Requirements and permissions

- macOS 14.0 or later, Apple Silicon and Intel.
- At least one supported agent, installed on the Mac.
- No Accessibility permission, no Screen Recording, and no network access for the watching itself. Bringing a terminal tab forward asks macOS for Automation permission for that terminal the first time, and only then.
- Notifications ask the system permission once, when you turn them on.

## Setup, in three steps

1. Download Ember and drag it into Applications. It is signed and notarized.
2. Click the flame, then the gear (or ⌘,), and press Install next to each agent you use. Until an agent is installed the panel says so and offers a button straight to Settings.
3. Start a session anywhere. The icon lights.

## Price and privacy

- Free forever. No account, no subscription, no purchases.
- No analytics, no telemetry, no tracking. Session data is JSON files on the Mac and never leaves it.
- The only network request is the Sparkle update check against a static file on this site.
- Full policy: https://stijnhanegraaf.com/ember/privacy/

## Performance

Native Swift. The panel's monitors and timers are torn down the moment it closes; there is no loop in the background. The port scan for LOCALHOST runs only while the panel is open. The menu bar icon animates at 24 frames per second only while a session is burning or needs you, and holds still under Reduce Motion.

## Download

- Download (DMG, notarized and Developer ID signed): https://stijnhanegraaf.com/ember/Ember.dmg
- Updates ship through Sparkle: https://stijnhanegraaf.com/ember/appcast.xml
- Changelog, every release: https://stijnhanegraaf.com/ember/changelog/
- Landing page: https://stijnhanegraaf.com/ember/

## Author

- Stijn Hanegraaf: https://stijnhanegraaf.com/ (this site's /llms.txt has the full profile)

This page (https://stijnhanegraaf.com/ember/) returns this file as Markdown when requested with `Accept: text/markdown`.

Last updated: September 2026
