All notable changes to gitbeacon are documented here.
Format follows Keep a Changelog. Versions follow Semantic Versioning.
1.2.0 - 2026-03-20
- Menu bar app event buffer raised from 100 to 500 events in memory.
- Scroll list shows last 50 events (was 30).
- Header counter now tracks total events since last Clear, not just the buffer size. The counter keeps climbing even after the 500-event buffer trims older entries.
- "Showing last 500" indicator appears when the event buffer is full.
- README and project docs updated for v1.1.2 dedup fixes, sync behavior, and test suite.
1.1.2 - 2026-03-20
- Terminal-state dedup: merged/closed PRs and closed issues now log exactly once.
New comments on a merged PR change
latest_comment_url, which passed the primary dedup and re-logged "Merged" on every subsequent comment. A secondary dedup check uses a state-stable key (id|Merged,id|PR closed, etc.) so the terminal event fires once regardless of later thread activity. just syncnow also patches the installed app bundle (/Applications/GitBeacon.app/Contents/Resources/) so the SHA-256 check ininstallDaemonScript()sees matching content. Previously, synced scripts were silently reverted on the next app launch because the bundle still had the old version.
tests/test-dedup.sh: 15 assertions across 7 test groups covering all three dedup tiers plus terminal-state secondary dedup. Run withbash tests/test-dedup.sh.
1.1.1 - 2026-03-17
- Duplicate PR comment and merge notifications: switched dedup key for
comment,pull_request_review, andauthorreasons fromid|updated_attoid|latest_comment_url. GitHub bumpsupdated_aton any thread activity (CI, commits, metadata), butlatest_comment_urlonly changes when new human-written content is posted. Merge fires once; subsequent CI runs on the same PR do not re-fire. - App update detection:
installDaemonScript()now compares SHA-256 instead of file size. Same-size edits are no longer silently skipped;just syncupdates are no longer reverted on the next app restart.
just dev-installrecipe: builds a universal binary, packages the.app, and installs it to/Applicationsin one command.just releasenow builds a fresh app and DMG before tagging, and installs locally after publishing. No stale bundle can end up in a release.
1.1.0 - 2026-03-15
- macOS menu bar app (
GitBeaconApp/): native SwiftUIMenuBarExtrathat wraps the daemon. Click the bell icon to see recent events, toggle sound, and quit. No terminal required.- Event log watcher using kqueue (
DispatchSource) with 5-second timer fallback for file rotation - Daemon lifecycle management: spawn, adopt existing, health check every 10s, kill on quit
- Single-instance guard: second launch exits immediately instead of creating duplicate menu bar icons
- Click any event row to open the GitHub URL in the default browser
- App icon (KingBee bee) in Finder, Spotlight, and DMG via
AppIcon.icns build/package-app.shassembles a proper.appbundle with icon,Info.plist, and ad-hoc codesigningbuild/package-dmg.shcreates a distributable DMG with drag-to-Applications install
- Event log watcher using kqueue (
- justfile recipes:
build-app(universal binary),package-app,package-dmg - CI workflow (
.github/workflows/ci.yml): shellcheck lint + swift build on macos-latest - README: Gatekeeper "unidentified developer" right-click workaround
- Daemon spawned with Homebrew bash 5 (not
/bin/bash3.2) to support associative arrays and-vtests used by the daemon script
1.0.0 - 2026-03-04
- Project renamed gh-notify → gitbeacon: CLI command (
gitbeacon), config directory (~/.config/gitbeacon/), script names (gitbeacon-daemon.sh,gitbeacon-bar.sh), notifier app (gitbeacon-notifier.app), and bundle ID (com.joryeugene.gitbeacon) - macOS notification permission prompt now shows GitBeacon instead of GH Notifier
- GitHub repo moved to
joryeugene/gitbeacon(old URL auto-redirects)
- Duplicate
review_requested,assign,invitation, andapproval_requestedevents: these one-shot notifications were re-firing on every PR update because GitHub bumpsupdated_aton all unread thread notifications whenever the PR is touched. Dedup now uses bare notification ID (notid|updated_at) for these reason types - PR review notifications showing as Assigned instead of actual review state (Approved / Changes requested) —
_pr_state_event()now called withno_fallback=1forassignandcommentreasons so the caller's default label is preserved when no review state is found
0.11.1 - 2026-02-28
- Bar: extracted 9 inline ANSI escape codes into named color constants (
C_GREEN,C_MAGENTA, etc.) - Daemon: extracted
_pr_state_event()helper, deduplicating PR review logic shared bypull_request_reviewandauthorreason handlers (net -27 lines) - Daemon:
seen-idsdeduped on boot viasort -uto prevent file bloat
- Bar: 📬 repo invitation events now render cyan (were falling through to dim)
- Daemon:
send_notificationescapes backslashes and double-quotes instead of stripping/replacing them, fixing osascript rendering of titles containing those characters
0.11.0 - 2026-02-27
scripts/demo-scenario.sh: standalone event writer that populates a realistic 8-event backlog with a fake daemon lock, enabling demo recordings withoutgh authdemo.tape: VHS recording script that producesassets/demo.gif(~22s animated GIF showing color-coded events arriving, stats bar, sound toggle, quit)assets/demo.gif: animated terminal recording for the README
- README hero section: replaced stale single-line text mockup (pre-v0.9.0 format) with animated GIF showing the live bar
just lintnow includesscripts/demo-scenario.sh
0.10.0 - 2026-02-27
- PR notifications (merges, approvals, state changes) silently dropped after first sight of a thread. GitHub's
/notificationsendpoint returns mutable threads, not immutable events; the daemon now tracksid|updated_atcompound keys so thread lifecycle updates register as new events. - Within-batch dedup collapsed distinct PR lifecycle events (e.g. approval + merge in the same poll window). Batch key now includes
reasonso each event type is processed independently.
seen-idsformat migrated from bare notification ID toid|updated_at. Old-format files are auto-detected and truncated on daemon startup (old entries can never match the new format).
0.9.1 - 2026-02-26
[t]test notification key removed; usejust notify "test"from the repo instead- README: add macOS notification screenshot
just releasenow creates a draft GitHub release automatically viagh release create --draft.claude/added to.gitignore
0.9.0 - 2026-02-26
- Event log now uses 2-line format: headline (timestamp + icon + label + repo) on line 1, title indented on line 2
- Bar shows 16 lines (8 events × 2 lines each) instead of 8 lines
- Event count uses
grep -c '^\['instead ofwc -lto avoid double-counting detail lines [o]URL extraction targets tab-bearing lines (header lines only)
- Antifragile daemon startup
pull_request_reviewevent handler- CI null-url edge case
0.8.1 - 2026-02-25
build-notifier/ installer: replaced terminal-notifier repackaging with a compiled ObjC binary (scripts/gh-notify-notifier.m). Nobrew install terminal-notifierrequired; prerequisite is now Xcode CLT (clang) +librsvg.- daemon/bar:
send_notificationand[t]handler now call thegh-notify-notifierbinary directly; removedterminal-notifierandicon.pngfallback paths. - install.sh: idempotency check now verifies
CFBundleDisplayName == "GH Notifier"andCFBundleExecutable == "gh-notify-notifier"— stale installs missing the display name are auto-rebuilt on re-run. - install.sh: Step 4 verify tests the custom app and opens System Settings automatically if permissions are denied.
- README: notification permissions section updated to reference "GH Notifier" throughout; verification command updated to call the custom app binary.
- Notification prompt / System Settings entry now shows GH Notifier instead of falling back to the executable name
gh-notify-notifier. - Icon cache flushed after build (
touch,iconservicesagent,lsregister,notificationcenteruirestart) so the bee icon and correct display name appear immediately in the permission prompt.
scripts/gh-notify-notifier.plist: authoredInfo.plistwithCFBundleDisplayName = "GH Notifier",CFBundleName = "GH Notifier",CFBundleIdentifier = "com.joryeugene.gh-notify".scripts/gh-notify-notifier.m: minimal Objective-C notification binary source (compilable via Xcode CLT, no Swift toolchain required).
0.8.0 - 2026-02-24
assets/icon.svg: KingBee — cute bee icon (amber body, black stripes, iridescent wings, big eyes, antennae, stinger) on GitHub dark background. The bee is the notification.just build-notifier: repackages terminal-notifier with the custom icon and bundle IDcom.joryeugene.gh-notify-notifier. Builds a full ICNS iconset viarsvg-convert+sips+iconutil, ad-hoc signs withcodesign, strips quarantine, triggers the first-launch notification permission prompt. Requiresbrew install librsvg.- README: bee icon displayed above badges
- daemon/bar: notification sender now prefers
gh-notify-notifier.appbinary when present, so the bee icon appears in the left-side notification slot. Falls back to systemterminal-notifier, thenosascript. - install: downloads GitHub mark PNG to
~/.config/gh-notify/icon.pngfor use as-contentImageon the right side of notifications
0.7.0 - 2026-02-24
terminal-notifieras primary notification path in daemon and installer;osascriptused as fallback when terminal-notifier is not installed. Fixes silent notification drops from within tmux sessions.[t]keybind: sends a test notification and shows a transient one-cycle status confirm in the barapproval_requestedevent type: 🚦 Approval needed (Tink, bold yellow in bar)invitationevent type: 📬 Repo invitation (Ping)- CI
skipped,neutral,staleconclusions: ⏭️ CI skipped (dim in bar) - PR closed (non-merged) via
authorreason: 🔒 PR closed (Funk) - Issue closed/reopened via
authorreason: 🔒 Issue closed / 🔓 Issue reopened - Distinct PR vs. issue comment labels via
authorreason: 💬 PR comment vs. 💬 Issue comment - 🔁 Changes requested label via
authorreason (review dismissed → requested changes state) - 🚦 included in session stats footer (approval_requested count)
- installer kills stale bar process after script deploy, prints relaunch prompt
- README: notification permissions section documenting terminal-notifier, one-time System Settings grant, DND gotcha, and bar-restart note
- installer test notification message updated to "Grant permission in System Settings if prompted"; follow-up
infolines added if no banner appears [t]added to keybind hints line in bar
0.6.0 - 2026-02-24
- Distinct sound mappings per event type: CI failure/action_required → Basso, CI pass/reopened → Pop, CI cancelled/closed → Funk, security alert → Sosumi; merge (Hero) and approve (Glass) unchanged
- Multi-sound dispatch: all distinct sounds in a poll batch now play sequentially in a background subshell instead of one priority-winner; a merge + CI fail in one poll plays Hero then Basso
- Stats line in footer showing per-icon session totals (🔀 ✅ 💬 ❌ 🟢) sourced from the full events.log; shown only when non-zero, resets when
[c]clears the log - Repo activity inline with stats: top-3 repos by event count appended after
│separator (e.g.org/repo(9) other/repo(3)) - Centered separator:
·:·[ gh-notify · N ]·:·dynamically pads to terminal width viatput cols; the+4correction accounts for 4 middle-dot chars (U+00B7) that are 2 UTF-8 bytes but 1 display column each
- Separator event count now reflects true all-time total from full events.log (
wc -l) instead of capped-at-8 tail count queue_sound()rewritten from priority-replacement to additive deduplicationplay_sound()helper removed (inlined into async dispatch block)
0.5.0 - 2026-02-23
[o]keybind to open the last event's URL in the default browserto_html_url()helper in daemon converts API URLs to browser-navigable HTML URLs:pulls/N→pull/N,check-suites/N→actions, empty →https://github.com/notifications- URL stored inline in events.log as tab-appended field:
[HH:MM] icon label - title (repo)\thttps://... - CI events always link to the repo's Actions page; PR/issue events use
html_urlfrom the GitHub API response when available
- Bar display loop strips tab-suffix before rendering (backward-compatible: lines without a tab are unchanged)
- Hints line updated to include
[o] open
0.4.0 - 2026-02-23
- CI/Actions events (
ci_activity): ❌ failed, 🟢 passed, ⚙️ running, ⛔ cancelled,⚠️ action required — each fetches the check suite for actual conclusion - State change events (
state_change): 🔒 closed, 🔓 reopened, 🔀 merged (via PR object fetch) - Team mention events (
team_mention): 👥 Team mentioned - Security alert events (
security_alert): 🛡️ Security alert (Glass sound) - Per-type color in bar for all new icons: red for ❌/⛔, green for 🟢, dim for ⚙️, cyan for 👥, yellow for 🔒/🔓, magenta for 🛡️
- Inline-header separator replaces fixed-width line:
── gh-notify ─ N ──auto-sizes to terminal width viatput cols - Event count shown live in the separator
- Events table in README expanded from 6 to 14 rows covering all handled reasons
- Layout comment updated to describe actual rendering behavior
0.3.0 - 2026-02-23
- Daemon liveness check in bar: yellow
⚠ daemon offlinewarning fires within 2s of crash [r]keybind to restart the daemon from the bar without relaunching the sessionjust uninstallrecipe to cleanly remove all installed files and state- Troubleshooting section in README covering the 5 most common failure modes
- Uninstall section in README with
seen-idsbackup note
- TLDR prerequisites moved before numbered steps as a runnable block (auth failure is now obvious before install)
- Diagram surfaces
sfx-stategate making the[s]sound toggle architecturally visible - Poll interval docs clarify default (30s) and minimum (15s) without contradicting each other
[r]restart waits for old daemon's lock release before spawning (prevented silent spawn failure)just lintSC2088 warning on~/.local/bindisplay string suppressed with inline comment
0.2.0 - 2026-02-23
- CLI wrapper
gh-notifyinstalled to~/.local/bin— no more barebashlaunch instructions justfilewithlint,install, andreleaserecipesjust notesrecipe to draft changelog sections from git log
- Notifications within a poll cycle are now batched and deduped by repo + title
- Sound dispatch uses priority queue: Hero > Glass > Ping > Tink
- Exactly one sound and one popup fires per poll — "N new notifications" when count > 1, eliminating popup spam under load
0.1.0 - 2026-02-23
- Background daemon polling GitHub notifications via
ghCLI - tmux status bar integration showing unread count
- macOS native popups via
osascript - macOS system sound notifications (Glass, Ping, Tink, Hero)
- Sound on/off toggle persisted to
~/.config/gh-notify/sfx-state seen-idsdedup across sessionsinstall.shwith prerequisite checks (gh, jq, tmux, osascript)