Skip to content

carlbrugger/opencode-ghostty-notify

Repository files navigation

opencode-ghostty-notify

Native macOS notifications for OpenCode with Ghostty tab focusing.

This project builds a tiny LSUIElement Swift app that owns macOS notifications through UNUserNotificationCenter, plus shell helper scripts that:

  • capture the Ghostty terminal target for an OpenCode session
  • suppress notifications when you're already on the matching terminal
  • dismiss stale notifications when you answer
  • focus the matching Ghostty terminal when you click a notification

Why

node-notifier currently relies on a bundled Intel-only terminal-notifier binary. This helper app avoids that dependency and uses native macOS notification APIs directly.

Project Layout

src/
  OpenCodeGhosttyNotify.swift
  Info.plist
plugin/
  index.ts
scripts/
  common.sh
  opencode-session-start.sh
  opencode-notify.sh
  opencode-dismiss.sh
install.sh
package.json

Use As A Plugin

This repo now contains a real OpenCode plugin module in plugin/index.ts.

For local development, build it first:

cd ~/Projects/opencode-ghostty-notify
bun install
bun run build

Then point OpenCode at the built package from your config:

mkdir -p ~/.config/opencode/plugins
cat > ~/.config/opencode/plugins/opencode-ghostty-notify.ts <<'EOF'
export { OpenCodeGhosttyNotifyPlugin as NotificationPlugin } from "/Users/carl/Projects/opencode-ghostty-notify/dist/index.js"
EOF

OpenCode will load that local plugin file automatically on startup.

If you eventually publish this package to npm, you can switch to the standard config-based plugin loading flow.

The exported plugin names are:

  • OpenCodeGhosttyNotifyPlugin
  • NotificationPlugin

Build

cd ~/Projects/opencode-ghostty-notify
bun install
bun run build
bash install.sh

Requirements:

  • macOS
  • Ghostty
  • Xcode Command Line Tools
  • Bun
  • sqlite3
  • jq optional, python3 used as a fallback for JSON parsing in shell scripts

The build output lands in:

build/OpenCodeGhosttyNotify.app

What Gets Stored

State is kept in:

~/Library/Application Support/com.opencode.ghostty.notify/store.db

Two tables are maintained:

  • sessions: maps session_id to Ghostty terminal_uuid, window_id, and tab_id
  • notifications: maps delivered notification ids back to session_id so dismiss operations can remove only the matching notifications

Entries older than 24 hours are purged automatically.

Scripts

scripts/opencode-session-start.sh

Reads JSON from stdin, extracts session_id, and stores the current Ghostty terminal target.

Example:

printf '%s' '{"session_id":"sess-123"}' | ./scripts/opencode-session-start.sh

scripts/opencode-notify.sh

Reads JSON from stdin, waits a short debounce period, suppresses if the target Ghostty terminal is already focused, then launches the native app.

Accepted top-level keys:

  • session_id
  • cwd
  • title
  • subtitle
  • message
  • notification_type
  • hook_event_name
  • delay_seconds
  • sound_name

Example:

printf '%s' '{
  "session_id":"sess-123",
  "cwd":"/Users/carl/.config/opencode",
  "message":"Permission required: bash",
  "notification_type":"permission.asked"
}' | ./scripts/opencode-notify.sh

scripts/opencode-dismiss.sh

Reads JSON from stdin and dismisses notifications previously posted for that session.

printf '%s' '{"session_id":"sess-123"}' | ./scripts/opencode-dismiss.sh

Integration Sketch

If you want to wire it manually, shell out to these scripts at the same lifecycle points you already use internally:

  1. session creation or first user message:
    • call opencode-session-start.sh
  2. notification-worthy event:
    • call opencode-notify.sh
  3. user answers or re-focuses the session:
    • call opencode-dismiss.sh

Minimal example payload:

{
  "session_id": "sess-123",
  "cwd": "/Users/carl/.config/opencode",
  "message": "Waiting for your answer",
  "notification_type": "question.asked"
}

Plugin Behavior

The bundled OpenCode plugin currently:

  • captures Ghostty session targets on session.created, session.updated, and user message.updated
  • dismisses notifications on user replies and session deletion
  • notifies on:
    • session.idle
    • session.error
    • question.asked
    • permission.asked

The plugin uses the shell helpers in this repo, and those helpers in turn launch the native app.

Behavior

  • notification clicks activate Ghostty
  • if a Ghostty terminal id was captured for the session, the app focuses that exact terminal
  • if only the window/tab ids exist, it falls back to selecting that tab
  • if no target is stored, it just activates Ghostty

Logs

Runtime logs are written to:

/tmp/opencode-ghostty-notify.log

Test

After building:

bun run check
./build/OpenCodeGhosttyNotify.app/Contents/MacOS/OpenCodeGhosttyNotify --test

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors