feat: blocking permission hook with FIFO-based approval#17
Merged
Conversation
When Claude Code's PermissionRequest hook fires, notify.sh now creates a FIFO and blocks reading from it. The notification carries the FIFO path; when the user clicks Allow (banner) or Enter (panel), the app writes "allow" to the FIFO. The hook reads the decision, outputs the proper PermissionRequest JSON to stdout, and exits — Claude Code skips its own UI prompt entirely. No keystroke injection, no AX hacks, no flaky timing. - notify.sh: create_perm_fifo() + wait_for_permission_response() functions use Python+select for a portable timeout-aware FIFO read - install.sh: PermissionRequest hook registered with timeout=600 (Claude Code's default) so the hook can wait long enough for user input - panel/EventStore.swift: NudgeEvent gains fifoPath - panel/EventListener.swift: decode fifo_path from socket payload - panel/Panel.swift: writeFIFO() helper; both UNUserNotificationCenter Allow action and panel Enter key write "allow\n" to the FIFO directly, short-circuiting the AppActivator path On timeout (no user response within 550s), the hook exits silently and Claude Code falls back to its terminal UI prompt. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
New 'Pin panel' toggle in Settings (on by default). When off, the panel auto-hides whenever it loses key focus — clicking outside, switching apps, etc. When on (default), the panel stays visible until explicitly dismissed via Esc, hotkey, or action. - PanelNav: panelPinned defaults true, persists to STACKNUDGE_PANEL_PIN - Settings: new toggle row 3, all subsequent rows shifted +1 (rowCount=11) - Panel: observes NSWindow.didResignKeyNotification on the panel itself and calls hidePanel() when fired and pin is off Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the keystroke-injection approach for permission approval (which has been unreliable across all our attempts: AX tree walking, ctrl+backtick, TIOCSTI) with Claude Code's documented hook decision protocol.
When the PermissionRequest hook fires, notify.sh now creates a FIFO at
/tmp/stack-nudge-perm-<id>.fifoand blocks reading from it. The notification carries the FIFO path. When the user clicks Allow (banner) or Enter (panel), the app writesallowto the FIFO. The hook reads it, outputs{"hookSpecificOutput": {"hookEventName": "PermissionRequest", "decision": {"behavior": "allow"}}}to stdout, and exits — Claude Code skips its own UI prompt entirely.Why this works
Claude Code's hook docs explicitly support this pattern:
This is the proper supported way. No keystroke injection. No AX hacks. No flaky timing.
Changes
notify.sh—create_perm_fifo()+wait_for_permission_response()use Pythonselectfor portable timeout-aware FIFO readsinstall.sh— PermissionRequest hook registered withtimeout: 600panel/EventStore.swift—NudgeEventgainsfifoPathpanel/EventListener.swift— decodesfifo_pathfrom socket payloadpanel/Panel.swift—writeFIFO()helper; UNUserNotificationCenterAllowaction and panelEnterkey both writeallow\nto the FIFO, short-circuiting the AppActivator pathTest plan
/tmp/after normal use🤖 Generated with Claude Code