Skip to content

feat(core): add macos-notifications skill - #33

Open
jawad-factory wants to merge 1 commit into
masterfrom
jawad/cli-1588-macos-notifications-skill
Open

feat(core): add macos-notifications skill#33
jawad-factory wants to merge 1 commit into
masterfrom
jawad/cli-1588-macos-notifications-skill

Conversation

@jawad-factory

Copy link
Copy Markdown

Description

Adds a macos-notifications skill to the core plugin, covering how to wire Droid CLI hooks to macOS desktop notifications and how to diagnose them when they silently do nothing.

The motivating problem is a failure mode that is effectively invisible. terminal-notifier, which most guides recommend, links against NSUserNotification (deprecated in macOS 11, no longer delivered in macOS 26), and its Homebrew bundle is adhoc, linker-signed with Info.plist=not bound and no TeamIdentifier, so macOS will not grant it notification authorization. Critically it returns exit 0 even when macOS discards the banner, so the hook reports success while delivering nothing. Version 2.0.0 is the final release (2017) and unmaintained, so there is nothing to upgrade to.

This was hit in practice: a correctly-wired Stop/Notification hook produced no banners while every diagnostic returned success.

The setup steps themselves are already in the public hooks docs, so a skill that only listed them would be redundant. The silent-failure behavior is documented nowhere, and an agent asked to fix notifications cold will install terminal-notifier, observe exit 0, and confidently report success, reproducing exactly the broken state. Encoding that trap is the point.

What the skill covers:

  • Diagnostic ordering, outside in, so the backend is checked before deeper spelunking
  • hooks.json vs settings.json precedence: when hooks.json exists, the hooks key in settings.json is fully ignored, making stale entries there dead config that would silently reactivate if the file were removed
  • nm and codesign invocations that prove the terminal-notifier diagnosis instead of asserting it
  • A known-good osascript reference hook script
  • The hook contract: never write stdout (Droid feeds hook stdout back to the agent as context), always exit 0
  • AppleScript escaping, including that backslashes must be doubled before quotes are escaped
  • tmux specifics: prefer #W over #S:#I because the window index shifts on close/reorder, plus automatic-rename being global-on but per-window-off once a window is named
  • That Notification Center's database is TCC-protected, so delivery cannot be verified programmatically and the human must confirm which banners appeared

Placed in core for discoverability, since that plugin is pre-installed by the CLI. Named and described as explicitly macOS-specific so it will not misfire for users on other platforms.

Related Issue

Closes CLI-1588

Potential Risk & Impact

Low. This is additive and documentation-only: one new SKILL.md, one skills/ symlink, and one README line. No existing skill, script, or workflow is modified, and nothing executes at install time.

Two things worth a reviewer's attention:

  • Scope claims. Findings were verified on macOS 26.5.2 only. The skill says so explicitly and instructs readers to re-run the nm/codesign checks rather than assuming older macOS behaves identically. No Linux notify-send guidance is included, because none of it was tested.
  • Tradeoffs of the recommended fix. osascript has no equivalent of terminal-notifier's -execute (click-to-focus) or -group (coalescing). The skill states both losses rather than presenting the swap as free.

How Has This Been Tested?

  • Repo CI (.github/workflows/check-skills.yml) extracted and run locally: All skills are accounted for., exit 0
  • Frontmatter parses as valid YAML, matching core's sibling convention of name + version + block description
  • Root skills/macos-notifications symlink resolves, and is recorded with mode 120000 matching the existing skills/review symlink
  • The reference script was extracted back out of the committed markdown and executed, so the shipped text is what was actually tested: exit 0 and zero stdout bytes across Stop, Notification, bare-env, and the non-tmux fallback path
  • Escaping verified against messages containing double quotes, backslashes, trailing backslashes, embedded newlines, and $(...)/backtick text, confirming it is passed through literally rather than evaluated
  • Every diagnostic command in the skill was run verbatim and produces the documented output
  • Staged diff scanned for credential patterns: clean

Environment: macOS 26.5.2 (25F84, arm64), terminal-notifier 2.0.0, tmux with iTerm2, bash 3.2.57 (the script deliberately avoids bash 4+ syntax).

End-to-end delivery was confirmed by a human observing the actual banners, since exit codes are unreliable here and the notification database is not readable.

Wiring Droid CLI hooks to macOS notifications has an invisible failure
mode: terminal-notifier links NSUserNotification, which macOS 26 no
longer delivers, yet it still exits 0 when the banner is discarded. The
hook looks healthy while delivering nothing.

The setup steps are already in the hooks docs; the silent failure is
documented nowhere, so an agent fixing this cold will install
terminal-notifier, see exit 0, and report success while leaving the user
broken. This encodes the trap, the diagnostic order, hooks.json vs
settings.json precedence, and a verified osascript reference hook.

Placed in core for discoverability, named macOS-specific so it will not
misfire for other platforms.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant