A macOS menu bar app for Claude Code permission prompts. When Claude pauses to ask before running something (say a git push --force, or an edit to a config file), Nudge pops a small panel out of the menu bar instead of asking back in the terminal. Click Allow from wherever you are.
It also ships with nudge-ask, a small CLI Claude can call when it needs a free-form text answer from you. Same popover style, with a text field instead of Allow/Deny.
Experimental: nudge-claude can launch Claude Code inside a tmux session that Nudge can mirror from the menu bar. That gives you a compact transcript and reply box while you test the app Claude is building.
Half the time im developing I run Claude Code on just my Mac screen. When Claude pauses to ask permission, the prompt shows up in whichever terminal Claude is running in. If I'm testing the app it just built, I don't see it for thirty seconds. Nudge surfaces those moments in the menu bar so I can answer without hunting.
It's a quality-of-life thing, not a security tool. When you're on one screen, you just don't want to keep tabbing back to the terminal to click Allow. Works in any Claude Code permission mode — default, accept-edits, plan, auto — whenever a tool call matches a pattern in ~/.config/nudge/patterns.txt. Anything matching pops up; anything else goes through the normal Claude Code flow.
One-line installer (builds from source):
curl -fsSL https://raw.githubusercontent.com/ZachDeLong/nudge/main/install.sh | bashManual source build:
git clone https://github.com/ZachDeLong/nudge.git
cd nudge
make installEither of those builds the app, copies it to /Applications/Nudge.app, seeds default patterns, wires Claude Code hooks into ~/.claude/settings.json, and launches Nudge in the background.
Pre-built bundle (no build step):
Grab Nudge.app.zip from the latest release. Unzip and drop Nudge.app into /Applications. Since the build is unsigned, run this once after copying:
xattr -dr com.apple.quarantine /Applications/Nudge.appThen clone the repo to wire the hook into Claude Code (you only need the scripts):
git clone https://github.com/ZachDeLong/nudge.git
cd nudge
./scripts/seed-patterns.sh # creates ~/.config/nudge/patterns.txt with defaults
./scripts/install-hook.sh # writes Nudge hook entries into settings.json
open -ga NudgeRequirements: macOS 14+ and jq (the hook installer reads and rewrites ~/.claude/settings.json). Source builds also need Xcode Command Line Tools (no full Xcode required). Install jq with brew install jq. The chat-mirror feature also needs tmux (brew install tmux).
make install also symlinks nudge-claude and nudge-update into a writable directory in your PATH (it tries /opt/homebrew/bin, /usr/local/bin, ~/.local/bin, then ~/bin), so you can just run them from anywhere.
nudge-update checks GitHub releases for a newer build:
nudge-update # print current vs latest, exit 0
nudge-update --check # exit 1 if an update is available (handy in scripts)
nudge-update --apply # download Nudge.app.zip, swap, relaunchReleases are produced by .github/workflows/release.yml on v* tag pushes. The workflow refuses to publish unless Resources-Info.plist's CFBundleShortVersionString matches the tag, so bump the plist before tagging.
Sparkle is on the roadmap once the project earns code signing.
Two halves:
- The app runs as a menu bar icon. It owns an
NSStatusItem, a popover, and a tiny localhost HTTP server. The server is how the hook talks to it. - The permission hook is a small Swift CLI at
Nudge.app/Contents/MacOS/nudge-hook. Claude Code runs it viaPreToolUse. It reads the tool call from stdin, checkspatterns.txt, and POSTs to the app with a local bearer token if there's a match. Then it blocks until you click Allow or Deny. - The agent hook is a non-blocking Swift CLI at
Nudge.app/Contents/MacOS/nudge-agent-hook. Claude Code runs it for lifecycle/tool events likeUserPromptSubmit,PreToolUse,PostToolUse,PostToolUseFailure,Notification, andStop, so Nudge can show whether a mirrored session is thinking, using a tool, waiting, idle, failed, or ended.
If the app isn't running when the hook fires, the hook auto-launches it via open -ga Nudge. If anything fails, the hook exits silently and Claude falls back to its normal terminal prompt.
On first launch, Nudge creates ~/.config/nudge/token with a random local bearer token. The server requires that token for /prompt and /ask, which keeps unrelated local processes from casually posting fake prompts if they discover the port.
~/.config/nudge/patterns.txt is the opt-in list. One rule per line; the hook re-reads it on every call, so edits take effect immediately.
Bash(git push:*) # prefix
Bash(*--force*) # infix (deny-leaning, won't promote)
Edit(/etc/**) # path glob
Mcp(playwright__*) # MCP server-wide
Full syntax, the chained-command and quote-normalization rules, "Always allow" promotion, and the import-from-permissions.ask flow are in docs/patterns.md.
Click the menu bar icon when there's no prompt up. The idle popover doubles as a settings panel:
- Pause Nudge / Resume Nudge. Master switch. Paused = the hook exits silently and Claude falls back to its native terminal prompt. The status pill and icon both reflect the current state.
- Skip when terminal is focused (on by default). When the frontmost app is a known terminal or IDE (Ghostty, iTerm2, Terminal.app, Warp, wezterm, Hyper, VS Code, Cursor), the hook skips the popover. You're already there; Claude's native prompt is fine.
- Quit Nudge. Exits the menu bar app entirely. The hook auto-launches it again on the next call.
Right-clicking the icon opens the same toggles as a context menu, in case that's the gesture you reach for.
Settings persist in ~/.config/nudge/prefs.json and are re-read on every hook call, so toggles take effect immediately. The local HTTP token lives next to it at ~/.config/nudge/token; you normally should not need to edit it.
A CLI Claude can call when it needs a free-form text answer.
/Applications/Nudge.app/Contents/MacOS/nudge-ask "Which deployment target?"
# popover with a text field appears
# user types "staging", clicks Send
# "staging" lands on stdoutExit code 0 with the answer on stdout. Exit code 130 if the user cancels. Anything else if Nudge is unreachable.
To opt Claude into using it, drop the skill into your skills directory:
cp -R skills/nudge-ask ~/.claude/skills/Or paste this into your Claude Code session and Claude will wire it up itself:
Add
Bash(/Applications/Nudge.app/Contents/MacOS/nudge-ask:*)to my~/.claude/settings.jsonpermissions.allow array. Then append this to~/.claude/CLAUDE.md: "When you need a free-form text answer from me, run/Applications/Nudge.app/Contents/MacOS/nudge-ask "<question>"via Bash and use stdout as my reply."
Either way, pre-allowing the Bash(...:*) rule keeps Claude from prompting before each call.
nudge-claude starts Claude Code inside a named tmux session and attaches your terminal to it. The session keeps running if you close the terminal, so you can re-attach later or chat from the Nudge popover.
nudge-claude # start a new session in the current directory
nudge-claude attach # re-attach to the most recent session
nudge-claude attach <id> # attach to a specific session (id from `list`)
nudge-claude list # list mirrored sessions
nudge-claude prune [days] # remove stale missing/ended records older than days (default: 7)
nudge-claude --help # show help(If nudge-claude isn't on your PATH, run /Applications/Nudge.app/Contents/MacOS/nudge-claude or re-run make install to create the symlink.)
Detach from a session without stopping it: Ctrl-b then d (the standard tmux detach binding). Claude keeps thinking; you can re-attach or chat via the menu bar.
Click the Nudge menu bar icon with no permission prompt active. The idle panel shows a session picker, current agent state, the recent transcript, and a reply box.
- Picker switches between active sessions. Labels show
kind · project · timeor whatever you've named them. - Pencil (✏️) renames a session. The name is saved to
~/.config/nudge/sessions/<id>.json. - Stop (⏹) kills the tmux pane and removes the session record.
- Refresh (↻) pulls the latest transcript. The panel also polls every 1.5s while open.
- Send with Enter. Shift+Enter inserts a newline.
- Status comes from Claude Code hooks when available, so Nudge can show tool use and idle/waiting state without guessing from terminal text.
Messages are forwarded to the tmux pane via a bracketed paste buffer, so multiline prompts survive and the terminal Claude session keeps its cwd, project context, skills, plugins, MCPs, and hooks.
Needs tmux and the claude CLI on your PATH. Install tmux with brew install tmux. Nudge checks the usual Homebrew tmux paths when the menu bar app is launched without your shell PATH; set NUDGE_TMUX_PATH if tmux lives somewhere custom.
Session metadata at ~/.config/nudge/sessions/*.json is owner-only (0o600) and validated on read. Nudge refuses to load a planted JSON from another local user. Messages sent through the chat input have control bytes and Unicode line separators stripped before reaching tmux, so a chat message can't deliver terminal escape sequences to the pane.
- Unsigned build. The Makefile runs
xattr -d com.apple.quarantineso it launches without Gatekeeper complaining, but the build isn't code-signed or notarized. If you download the prebuilt zip, you'll need to run thatxattrcommand yourself once. - One Mac at a time. Patterns aren't synced across machines.
- Hooks fire before Claude classifies. That's why patterns are explicit opt-in rather than "everything auto mode would prompt about."
PreToolUseruns before Claude decides whether a call would trigger a prompt, and thePermissionRequestevent (which fires at the right time) is observe-only. - Queue is FIFO with a 5-minute timeout. Pile up enough prompts and the older ones expire.
- Agent session mirroring requires
nudge-claude. Nudge can cleanly mirror sessions it launched through tmux; it does not attach to arbitrary existing terminal tabs.
cd /path/to/nudge && make uninstallRemoves /Applications/Nudge.app, kills the running app, removes runtime port/token files, and cleans the hook out of settings.json (with a backup). Your patterns and prefs are left in ~/.config/nudge/.
MIT. See LICENSE.



