Beta: Pager is pre-1.0. Backward compatibility is not guaranteed until version 1.0.0 is reached.
A tiny macOS agent that delivers notifications you can click to jump back to the exact place that paged you, and clear the moment you return - driven from a shell one-liner or a tmux hook.
It exists because osascript display notification is a dead end: notifications
are attributed to Script Editor (so clicking lands you there), and there is no
way to withdraw one once delivered. Pager owns its own identity and a real
click handler, and exposes a remove command so a page disappears as soon as
you are looking at what it was about.
- Posts a notification under its own identity (its icon, not Script Editor's).
- Click to focus: a page carries a tmux session and window id; clicking it switches the most recently used client to that window and brings the terminal (Ghostty) forward.
- Programmatic dismissal:
pager remove --key <id>withdraws a delivered page, so a tmux focus hook can clear it when you return to its window. - Per-page thumbnails:
--imageattaches an image so different callers can look distinct without changing the app icon.
The daemon runs as a login-item agent (no Dock icon, no menu bar). A single binary is both the agent and the CLI.
- macOS 15+
- tmux and Ghostty for the click-to-focus action (Pager still posts and clears without them)
brew install pszypowicz/tap/pagerThe cask symlinks the pager CLI onto your PATH and registers the agent to
launch at login. On first launch, allow notifications when macOS asks.
pager post --key <id> [--title T] [--subtitle S] [--body B]
[--sound NAME] [--image PATH] [--session-id SID] [--window-id WID]
pager remove --key <id>
pager --version
--key is the notification's identity and its dedup/removal key. Pass the tmux
#{window_id}: it is server-unique and stable across renumber-windows, so a
page still matches its window at dismiss time.
Post a page for a background tmux window:
pager post --key @7 --title "Attention" --subtitle "work / window 3" \
--body "waiting for input" --sound Funk --session-id '$2' --window-id @7Clear it when you return to that window (from a tmux hook):
set-hook -g pane-focus-in 'run-shell -b "pager remove --key #{window_id}"'
set-hook -g after-select-window 'run-shell -b "pager remove --key #{window_id}"'A launchd-started agent does not inherit your interactive PATH, so use an
absolute path to pager in the hook if pager is not resolvable there.
The agent logs the page/click/remove lifecycle to the unified log under the
cz.szypowi.pager subsystem. Most of the flow is logged below the persisted
level, so watch it live with --level debug:
log stream --level debug --predicate 'subsystem == "cz.szypowi.pager"'make build # build and sign Pager.app into .build/
make install # build, then copy to /Applications
make dev # ad-hoc build and (re)launch the agent for local testingscripts/generate-icon.swift regenerates the app icon.
MIT