agentic-coding-notify
macOS notification preferences for local agent apps and CLIs.
This repo ships one shared notification layer with four entrypoints:
- Claude Code through the backwards-compatible
claude-code-notify plugin.
- Codex through the
notify = [...] hook in ~/.codex/config.toml.
- OpenCode through a dedicated adapter script.
- Pi through a dedicated adapter script.
The repo name is agentic-coding-notify; the Claude plugin name intentionally remains claude-code-notify so existing Claude installs keep working.
Quick start
Install the macOS notification dependency:
brew install terminal-notifier
Run the local preferences UI:
python3 web/notify_ui.py --open
Open http://127.0.0.1:8765, choose the voice/sounds/text templates, test samples, then save or export the config.
Install the adapter you use:
What it does
When an adapter fires, you get:
- a desktop notification via
terminal-notifier when available;
- a configurable notification sound;
- an optional start sound;
- a short spoken label using macOS
say;
- an optional end sound.
The spoken text is context-aware:
- app sessions can say just the app name, e.g.
Codex App;
- CLI sessions can combine the service plus a short tab/profile label, e.g.
Codex One;
- Codex App is detected explicitly and speaks as
Codex App, not a truncated CLI label.
Local preferences UI
The local web UI is light-mode-only and uses a retro N64-gray console-panel style. It is self-contained: no external CSS, no external icon packs, and no remote assets.
It supports:
- searchable, scrollable comboboxes for voices, sounds, templates, and service names;
- type-to-filter fields that still show the full option list when focused;
- combobox show-off modes: cycle every option once, or enter
SHOW OFF SELECTION, pick values, then cycle only those values once;
- an
× clear control for show-off selections;
- one-button Play/Stop sample toggles for voice and every sound field;
- automatic sample playback when changing voice or sound selections;
- a standard voice sample phrase:
Agentic Coding Notify;
- speech-rate tuning with
say -r words per minute;
- a live preview for service, tab label, text, voice, and sounds;
- visible confirmation feedback for save, export, dry-run, notification, preset, sample, and show-off actions;
- hover/focus tooltips on each button and action;
- export of the current UI state to
agentic-coding-notify-config.json;
- disclosure guidance for terminal labels;
- a CLI-only toggle to speak or omit the terminal tab title.
Preferences are saved to:
~/.agentic-coding-notify/config.json
Default config:
{
"voice": "Zarvox",
"rate": "250",
"notification_sound": "Submarine",
"start_sound": "Basso",
"end_sound": "Submarine",
"app_voice_text_template": "{service} App",
"cli_voice_text_template": "{service} {label}",
"voice_text_template": "",
"speak_tab_title": "true"
}
Template placeholders:
| Placeholder | Meaning |
|---|
{service} | Claude, Codex, OpenCode, or Pi |
{label} | app label, terminal tab/profile name, or cwd fallback |
{voice_label} | adapter-computed fallback spoken text |
{message} | agent message preview used in tests/templates |
{context} | app or cli |
More UI details: docs/web-ui.md.
Running and stopping the UI
Run in the foreground:
python3 web/notify_ui.py --host 127.0.0.1 --port 8765
Or run and open the browser automatically:
python3 web/notify_ui.py --open
Stop a foreground server with Ctrl-C. If a background server is listening on the default port:
lsof -tiTCP:8765 -sTCP:LISTEN | xargs kill
Check whether it is still running:
lsof -iTCP:8765 -sTCP:LISTEN -n -P
On this Mac the local Python UI process measured about 18 MB RSS while serving the page. Voice and sound option lists are cached after the first /api/options request to avoid repeated macOS scans.
Repository layout