A lightweight, native macOS system tray app that keeps you informed about your GitHub pull request activity — without ever opening a browser.
Think Graphite Bar, but open-source and powered by the GitHub CLI (gh).
- Lives in your menu bar. No dock icon. No main window. Just a tray icon with a badge count.
- PR lifecycle awareness. PRs are categorized into smart buckets:
- Needs Your Review
- Changes Requested on Yours
- Approved, Ready to Merge
- CI Failing
- Waiting for Review
- Reviewed by Others
- Drafts
- Round author avatars in the tray menu for quick identification.
- Native notifications when PR states change (new review requests, approvals, CI failures).
- Fine-grained notification control — pick exactly which events fire a notification and which ones play a sound, independently. Master toggles still act as kill-switches.
- Custom notification sound — choose any built-in macOS system sound (Glass, Pop, Tink, Bottle, Frog, Funk, Hero, Morse, Ping, Purr, Sosumi, Submarine) or point at your own
.aiff/.wavfile. Preview button included. - Last-notified indicator — each PR row in the tray shows when you were last pinged about it (e.g.
🔔 5m), so you know whether something has been sitting unread. - Per-bucket sort order — sort each section independently by Updated, Last Notified, Created, or PR number, ascending or descending.
- Configurable polling — set your own interval (default: 120s, minimum 30s).
- Repo & org filtering — block-list repos (or entire organizations) you don't care about via a settings UI with an org/repo tree. Muting a whole org also mutes any future repos under it.
- Bucket visibility, order, and badge contribution — show/hide sections, drag to reorder, and pick which sections count toward the tray badge.
- Launch at login — starts silently in the background.
- Lightweight — small Rust binary, minimal resource usage.
Area.Settings.mp4
GH Tray uses the GitHub CLI (gh) to run GraphQL queries against the GitHub API. It categorizes your PRs by their lifecycle state and surfaces them in a native macOS menu. No OAuth tokens to manage — it piggybacks on your existing gh auth session.
Tray Icon → Badge Count (unread PRs)
↓
Click → Native Menu (PRs grouped by bucket)
↓
Click PR → Opens in browser
- Go to Releases
- Download the
.dmgfor your architecture:- Apple Silicon (M1/M2/M3/M4):
GH Tray_x.x.x_aarch64.dmg - Intel:
GH Tray_x.x.x_x64.dmg
- Apple Silicon (M1/M2/M3/M4):
- Open the
.dmgand drag GH Tray to your Applications folder
Important
macOS Gatekeeper warning: Since GH Tray is not notarized by Apple, macOS may block it from running. To fix this, run:
xattr -dr com.apple.quarantine '/Applications/GH Tray.app'This removes the quarantine flag added by macOS when downloading apps from the internet. Only do this if you trust the source (i.e., you downloaded from this repository's releases page or built it yourself).
Prerequisites:
- Rust (stable, edition 2024)
- GitHub CLI (
gh) — installed and authenticated (gh auth login)
# Clone the repository
git clone https://github.com/felps-dev/ghtray.git
cd ghtray
# Build
cargo build --release
# The binary is at target/release/ghtray
# Or build the .app bundle:
cargo install tauri-cli --version "^2"
cargo tauri buildGH Tray requires the GitHub CLI to be installed and authenticated:
# Install gh (if you haven't already)
brew install gh
# Authenticate
gh auth loginGH Tray will detect if gh is missing or unauthenticated and guide you through setup.
- Launch GH Tray — it appears as an icon in your menu bar
- The badge count shows how many PRs need your attention
- Click the tray icon to see PRs grouped by lifecycle state
- Click any PR to open it in your browser
- Right-click or use the menu to access Settings or Refresh
Access settings from the tray menu. The window is organized into tabs:
- General — auto-start, custom
ghCLI path (auto-detected by default) - Polling — interval (default: 120s, min 30s), merged PR window, max PR age cutoff
- Notifications — master toggles, sound picker with Preview, and a per-event matrix letting you choose which transitions ring and which play a sound
- Sections — visibility, drag-to-reorder, per-bucket sort key, and which sections count in the tray badge
- Repositories — org/repo block-list. Unchecking a single repo hides just that repo; unchecking a whole org mutes every repo under it, including ones that appear later.
- About — version, log file, source link
ghtray/
├── crates/ghtray-core/ # Core library (models, GitHub API, config, state)
│ └── src/
│ ├── models.rs # GraphQL types, Bucket enum, CategorizedPr
│ ├── github.rs # Fetch, categorize, filter, diff
│ ├── config.rs # AppConfig (poll interval, blocked repos)
│ └── state.rs # State persistence
├── src-tauri/ # Tauri app (tray, menu, polling, commands)
│ └── src/
│ ├── lib.rs # Tray setup, menu builder, polling
│ └── main.rs # Entry point
└── ui/
└── settings.html # Settings window (vanilla HTML/CSS/JS)
Key design decisions:
- Single GraphQL query with 4 aliased searches (only 5 rate limit points)
latestReviewsfor deduplicated per-reviewer state- Dedup PRs by node ID across search results
@mein search queries (no username needed)- Block-list for repos (new repos show by default)
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes
- Push to the branch
- Open a Pull Request
This project is licensed under the MIT License — see the LICENSE file for details.
- Inspired by Graphite Bar
- Built with Tauri v2 and Rust
- Powered by the GitHub CLI
