A small command line tool that puts a notification in a tmux popup, over whatever window is on screen.
Anything that runs in a pane nobody is watching — a long build, a script that
needs a decision, a coding agent waiting on an answer — can only speak in its
own pane, and goes unread until someone comes back to it. tnotify draws the
message as a popup instead, waits for an answer, and prints it to stdout.
- Shows a message in a tmux popup, sized and placed around the text.
- Asks questions: pick one option, tick several, or type a free text answer.
- Blocks until answered, so the caller can act on the reply.
- Keeps anything set aside, to be answered later rather than lost.
- Lists everything waiting in a side panel, where it can be answered or cleared.
- Publishes the number of waiting notifications to tmux, for a status line.
- Colours, popup position, side panel and cursor markers all come from a TOML config.
This repository is its own Homebrew tap, so the tap is named for it rather
than for a separate homebrew- repository:
brew tap karnull/tnotify https://github.com/karnull/tnotify
brew install tnotify
The formula carries a prebuilt binary for macOS and Linux on both Intel and Apple silicon, so nothing is compiled and Go is not needed to install it.
Building needs Go 1.26.5 or later, and running it needs tmux 3.2 or later, the
first release with display-popup.
$ make build
$ ./build/tnotify --helpSend a notification and carry on:
tnotify notify "Build finished, 43 tests pass." --head Done
Ask a question and read the answer off stdout:
answer=$(tnotify notify "Deploy to production?" --head Confirm --interactive "yes" "no")
Deal with what is waiting:
tnotify show --all # side panel of everything queued
tnotify show --last # raise the one set aside most recently
tnotify clear --all # throw the queue away unanswered
ScriptingGuide.md covers the rest: gating on whether there is anywhere to draw, what stdout says about how a question ended, waiting for late answers, and reading the waiting count into a status line.
tnotify ships with a skill that teaches a coding agent to ask through the
popup rather than in its own pane, which is where its questions normally go
unread.
tnotify --skill # where the skill file goes, and how to allow it
tnotify --skill-export # the skill itself, to write into a skills directory
Questions carry an author, so several agents can be running at once and still be told apart, and one of them cleared without touching the rest.
AgentGuide.md covers installing the skill for Claude Code, Codex, Copilot CLI and Antigravity, and what an agent has to be told about answers it never receives.
tnotify --config # print the config path, creating it if there is none
tnotify --defaults # back up the current config and write the defaults
↑ ↓ or j k move · space ticks an option under --multiple · enter
answers · esc sets aside for later · del discards.
$ make test$ make format # format in place
$ make format-dry-run # list files that need formatting- Project Structure: repository layout and module responsibilities
- ScriptingGuide.md: driving tnotify from scripts
- AgentGuide.md: setting up the skill for coding agents
make release builds the project for each release target chosen when it was generated (linux-arm64, linux-x64, macOS-intel, macOS-apple-silicon), writing one binary per target into dist/. With no targets, dist/ is left empty.
The targets are set in the Makefile, in RELEASE_TARGETS, and built by scripts/release.sh.
Merging into main with a new version in VERSION publishes a GitHub release
carrying those binaries, and the Homebrew formula is then pointed at it.
The project uses Copier to manage its template. When the template is updated, the project can be easily brought up to date by running the following command from the project root:
$ copier update --defaults --answers-file .copier-go-answers.ymlWhen the --defaults option is used, copier update skips all previously answered questions and prompts only for newly introduced ones.