
Notify
- 2 repo stars
- Updated July 24, 2026
- cboone/agent-harness-plugins
Get a macOS notification when Claude Code, OpenCode, or Codex CLI finishes a task or needs your attention.
About
Notify sends macOS desktop notifications when an AI coding agent — Claude Code, OpenCode, or Codex CLI — finishes a task or pauses needing input. It lets a developer step away from a long-running agent session and get pinged the moment it completes or stalls. A small workflow/alerting utility for agent-driven development.
- macOS desktop notifications
- Works with Claude Code, OpenCode, Codex
- Fires on task completion
- Alerts when attention needed
Notify by the numbers
- Data as of Jul 25, 2026 (Skillselion catalog sync)
/plugin marketplace add cboone/agent-harness-plugins/plugin install notify@agent-harness-pluginsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| repo stars | ★ 2 |
|---|---|
| Last updated | July 24, 2026 |
| Repository | cboone/agent-harness-plugins ↗ |
What it does
Get a macOS notification when Claude Code, OpenCode, or Codex CLI finishes a task or needs your attention.
README.md
Notify (macOS)
Sends rich, harness-aware macOS notifications when Claude Code, OpenCode, or Codex CLI finishes a task or needs your attention. Click a notification to focus the originating terminal app and tmux pane.
Type: Hook
Requires: alerter (>= 26.5). Install via Homebrew: brew install vjeantet/tap/alerter. Also requires jq and macOS tmux (only used when running inside a tmux session).
Installation
Claude Code
See the marketplace install instructions.
Using with Codex CLI
codex plugin marketplace add cboone/agent-harness-plugins
Enable plugin-bundled hooks once per host so the Stop hook fires:
codex features enable plugin_hooks
Without this flag the plugin installs successfully but the hook is silently ignored. See Codex CLI known limitations for context.
Refresh the marketplace after repository updates:
codex plugin marketplace upgrade agent-harness-plugins
Codex's hook event enum does not include Notification or PreCompact, so the plugin only wires the Stop event on Codex. PermissionRequest is intentionally not wired: that hook runs in the automatic-policy path before Codex's user approval UI is shown, not as a user-facing prompt, so notifying on it would alert the human about decisions Codex's internal approver is already making. For idle / elicitation / compact-style banners and approval prompts, enable Codex's native tui.notifications = true in ~/.codex/config.toml (the two are complementary; both can run at once).
Using with OpenCode
OpenCode loads the plugin automatically when OPENCODE_CONFIG_DIR is set to this repository's dist/opencode/ mirror. The TypeScript plugin lives at opencode/index.ts and dispatches on OpenCode's event stream rather than Claude Code's named matchers.
OpenCode's event model differs from Claude Code's, so the parity is approximate:
| OpenCode event | Notification | Claude Code equivalent |
|---|---|---|
session.idle |
Task completed | Stop (with project + branch + last user/assistant turn) |
permission.updated |
"Needs permission…" | Notification (permission_prompt, elicitation_dialog) |
experimental.session.compacting |
"Auto-compacting…" | PreCompact (auto) |
The standalone "Waiting for input…" notification (Claude Code's Notification:idle_prompt) is not separately representable: OpenCode's session.idle already carries the Stop semantics. The compacting hook depends on an experimental OpenCode API and may break on upgrades.
Granting notification permission
alerter posts notifications by impersonating Terminal's bundle identity (the v26.4+ default) so it appears under "Terminal" in System Settings → Notifications, where you grant alert permission once. You will not see a separate "alerter" entry. The first notification after install may not appear until permission is granted; the second will.
What it does
Delivers native macOS notifications so you can work in other apps while an agent runs. Each notification carries:
- A per-harness icon: Claude Code, OpenCode, or Codex's app icon.
- A subtitle that identifies the task: when running inside tmux with a custom pane title (set by
workmuxor similar), the subtitle is<project> · <pane title>. Otherwise,<project> · <branch suffix>, where the branch suffix is everything after the first/(sofeature/improve-notifierbecomesimprove-notifier). - An informative body: per-event content (see matrix below). Claude Code permission events show a per-tool preview (the Bash command, the file path being edited, etc.) reconstructed from the most recent
tool_useblock in the transcript, since the Notification payload itself omits tool details. OpenCode permission events use OpenCode's pre-computedtitle, falling back topatternor per-toolmetadata. Claude Code and OpenCode Stop events show<last user message> → <last assistant message tail>; the Claude Code assistant tail comes from the Stop payload'slast_assistant_messagerather than from a transcript walk. Codex Stop is the last assistant message alone, also taken from the payload. - A per-event sound (Tink for Claude Code idle/elicit and Codex done, Funk for permission and OpenCode error, Pop for auto-compact, Glass for Claude Code and OpenCode done). Codex Stop uses the soft Tink rather than the louder Glass because Codex fires
Stoponce per "no follow-up needed" sampling cycle in its turn loop, so a single user turn can produce multiple notifications when Stop hooks request continuation. Each Stop updates the same notification group, so successive firings replace the previous banner in place rather than stacking. - A per-event group so a fresh notification dismisses any prior notification of the same kind, instead of stacking.
- Click-to-focus: clicking the body of any notification activates the originating terminal app (auto-detected from
$TERM_PROGRAM, supports Apple Terminal, iTerm2, Ghostty, WezTerm, VSCode, Alacritty) and, if you were inside tmux when the hook fired, switches the tmux client to the originating session, window, and pane.
When it fires
Claude Code
| Event | Title | Body | Sound |
|---|---|---|---|
Notification:idle_prompt |
Claude Code · Idle |
Last assistant message tail (fallback Waiting for input) |
Tink |
Notification:elicitation_dialog |
Claude Code · Question |
The actual question text from the payload | Tink |
Notification:permission_prompt |
Claude Code · Permission |
<Tool>: <preview> (subtitle is suffixed with the tool) |
Funk |
PreCompact:auto |
Claude Code · Compacting |
Auto-compacting context |
Pop |
Stop |
Claude Code · Done |
<last user message> → <last assistant message tail> |
Glass |
Codex
| Event | Title | Body | Sound |
|---|---|---|---|
Stop |
Codex · Done |
last_assistant_message from the payload |
Tink |
Codex fires Stop once per "no follow-up needed" sampling cycle in its turn loop (codex-rs/core/src/session/turn.rs), so a single user turn may produce multiple Stop notifications when Stop hooks request continuation. The notification uses the soft Tink sound and reuses the codex-stop group, so successive firings within one turn dismiss the prior banner rather than stacking. Click the body to focus the originating pane.
OpenCode
| Event | Title | Body | Sound |
|---|---|---|---|
permission.updated |
OpenCode · Permission |
OpenCode's pre-computed title, falling back to pattern or per-tool metadata |
Funk |
session.error |
OpenCode · Error |
error.data.message (or the error name) |
Funk |
experimental.session.compacting |
OpenCode · Compacting |
Auto-compacting context |
Pop |
session.idle |
OpenCode · Done |
<last user message> → <last assistant message tail> |
Glass |
Click-to-focus details
When you click the body of a notification, the plugin runs scripts/focus-pane with the captured terminal program and tmux state. The script does two things.
Activate the host terminal app via osascript -e 'tell application id "<bundle-id>" to activate'. Bundle IDs are derived from $TERM_PROGRAM:
$TERM_PROGRAM |
App |
|---|---|
Apple_Terminal |
Terminal.app |
iTerm.app |
iTerm2 |
ghostty |
Ghostty |
WezTerm |
WezTerm |
vscode |
VS Code |
alacritty |
Alacritty |
| (anything else) | Terminal.app |
Switch the tmux client if the hook fired inside tmux: tmux switch-client -t <session> \; select-window -t <session>:<window> \; select-pane -t <session>:<window>.<pane>.
Failures (closed pane, no client attached, missing terminal app) are silent: clicking a notification should never produce a visible error.
Notes and caveats
alerterblocks waiting for user interaction, so every event launches it in a detached subshell. The harness is never held up. Each invocation has a 24-hour timeout to prevent orphaned processes from accumulating.- The transcript-based extractors (last user message, last assistant message tail, pending tool use) iterate the transcript JSONL. Performance is fine for typical sessions; very long transcripts may add a small delay before the notification appears.
- The
--app-iconflag uses a private macOS API thatalerterkeeps working release to release. If a future macOS update breaks it, notifications will still fire but with the default Terminal icon.