Are you the author? Sign in to claim
A quiet macOS notch indicator for Claude Code, OpenCode, and Codex CLI sessions
Know when your coding agents need you—without leaving the notch.
Moonglade is a quiet, native macOS indicator for Claude Code, OpenCode, Codex CLI, Pi, and Convoy pipeline sessions. It lives around the MacBook notch (or as a pill on displays without one) and returns you to the exact terminal tab or tmux pane with one click.
| State | In the bar | Meaning |
|---|---|---|
| Running | Braille dot-matrix spinner | The agent is processing. |
| Waiting | Green dot | The session waits at the prompt for your next input. |
| Blocked | Red dot | A question, permission ask, or failed pipeline step needs you. |
Running and waiting sit on the left of the camera and blocked on its right, and each pair puts its round glyph on the outer screen edge so both wings meet the notch shoulders symmetrically. The bar shows a count for each state that actually exists right now — zero-count states leave no slot behind — and collapses to a single dimmed moon when no agent is running at all.
The menu always shows the real status and provider per session, using the same three glyphs plus a grey dot for a session whose process has exited and is about to be reaped.
Apple Silicon is the tested development platform. Intel builds have not yet been validated.
One command builds the app, installs it into /Applications, wires the agent hooks, launches it, and verifies everything:
git clone https://github.com/ixjosemi/Moonglade.git
cd Moonglade
./scripts/install.sh
The same command reinstalls: it stops the running instance, replaces the app, relaunches, and re-verifies. The bundle is ad-hoc signed for local use — do not redistribute it as an official release.
Verify an existing installation at any time:
/Applications/Moonglade.app/Contents/Resources/bin/moonglade doctor
✓ hook binaries: all executables present in ~/.moonglade/bin
✓ state directory: ~/.moonglade/state exists
✓ Claude Code hooks: all lifecycle hooks registered in ~/.claude/settings.json
✓ OpenCode plugin: ~/.config/opencode/plugins/moonglade.js matches the bundled file
✓ Codex notify: notify hook registered in ~/.codex/config.toml
✓ Pi extension: ~/.pi/agent/extensions/moonglade.ts matches the bundled file
doctor is read-only and exits non-zero when something is broken, so it is also usable from scripts.
For development without touching /Applications:
swift build
swift run moonglade-tests
./scripts/build-app.sh
open .build/Moonglade.app
SwiftPM cannot compile Metal sources, so the expansion ripple ships as a prebuilt Sources/MoongladeApp/Resources/default.metallib. After editing Ripple.metal, regenerate it with ./scripts/compile-shaders.sh and commit the result.
Without integrations the app still detects running agents (via a fast libproc process scan), but every session shows as permanently working — the hooks are what feed real status changes. install.sh runs moonglade install, which:
~/.moonglade/bin;~/.claude/settings.json, preserving every existing setting and hook (the merge is idempotent);~/.config/opencode/plugins/moonglade.js only when it can do so safely;notify entry only when no notification command exists;~/.pi/agent/extensions/moonglade.ts only when it can do so safely.Installation fails instead of replacing an unknown Moonglade-named plugin. Integration directories may be symlinks — common in dotfile setups — as long as they resolve to a directory you own inside your home; ~/.moonglade itself must be symlink-free because hooks execute binaries from it. Agents started before installing need a restart to pick up the hooks. OpenCode additionally loads plugins in its detached background service, which survives TUI restarts — after installing, run pkill -f "opencode2 serve" once; the next opencode starts a fresh service with the plugin loaded.
To remove integrations and local state:
/Applications/Moonglade.app/Contents/Resources/bin/moonglade uninstall
Then quit Moonglade and delete the app bundle. Review your Claude or Codex configuration if you manually modified Moonglade entries after installation.
| Host | Focus strategy | Notes |
|---|---|---|
| Ghostty | exact surface ID, foreground PID, or TTY; unique project/title fallback | Requires Ghostty 1.3+ |
| cmux | exact panel ID, no fallback | Resolved before Ghostty: cmux ships Ghostty's engine and reports the same TERM_PROGRAM |
| iTerm2 | exact normalized session ID, then TTY | Selects the split, tab, and window |
| Terminal | exact TTY | Selects the tab and raises its containing window |
| tmux | validated pane ID, then host activation | tmux must be in a trusted standard install location |
macOS asks for Automation access the first time Moonglade controls a terminal. If denied, enable it under System Settings → Privacy & Security → Automation.
Claude hooks, an OpenCode plugin, a Pi extension, the Codex rollout watcher, the Convoy runs watcher, and a process fallback produce versioned session documents under ~/.moonglade/state. The app observes that directory and renders active sessions. State is written atomically with user-only permissions. Convoy needs no hook at all: its run metadata under ~/.convoy/runs is read directly, and a run is only shown while its recorded server process is verifiably alive. OpenCode phase IDs named by Convoy are retained in a private ownership index and filtered at repository load time, so internal phases stay hidden even after a plugin rewrite or app restart.
Everything is event-driven and off the main thread: a libproc-based scanner (no subprocesses, ~2 ms per full sweep) runs on a 5-second heartbeat, kernel EVFILT_PROC exit watchers reap closed sessions instantly, and directory observation with debounce delivers state changes to the UI. A native session that has been quiet for a full scan interval is also checked against the detected agent set; removal requires two consecutive misses, so one transient metadata-read failure cannot hide a live session. Terminal identity disambiguates agents sharing a project directory. Claude and OpenCode status changes land in well under a second; Codex and Convoy ride the heartbeat. Session titles follow the live Ghostty tab title — cleaned of status decorations, then truncated by the row's width rather than a fixed character count — and a manual rename (persisted in ~/.moonglade/session-names.json) always wins. Agent matching accepts either the kernel-resolved executable path or argv[0], so versioned symlink installs like ~/.local/bin/claude → …/versions/x.y.z are detected correctly.
See Architecture for the full data flow and trust boundaries.
Moonglade has no networking or telemetry. It stores local session metadata—including project paths, process IDs, timestamps, and terminal identifiers—but not prompts or model responses. Read PRIVACY.md before installing integrations and SECURITY.md before reporting a vulnerability.
Treat moonglade debug output as private because it includes session and project metadata:
/Applications/Moonglade.app/Contents/Resources/bin/moonglade debug
swift run moonglade-tests.Read CONTRIBUTING.md and AGENTS.md. New runtime behavior requires a failing behavioral test first. All pull requests must pass:
swift build
swift run moonglade-tests
./scripts/build-app.sh
Moonglade is independent and is not affiliated with Anthropic, OpenAI, SST, Ghostty, Apple, or tmux. Product names and marks identify compatible tools only. See NOTICE.
MIT © 2026 Josemi Hernandez
Design enforcement with memory — keeps your UI consistent across a project
Create animation-rich HTML presentations using a coding agent's frontend skills
1000+ skills curated from Anthropic, Vercel, Stripe, and other engineering teams
Detects 37 AI writing patterns and rewrites text with human rhythm across 5 voice profiles