Menu bar (SwiftBar)
macOS menu bar integration for monitoring stacks and switching worktrees.
Menu bar (SwiftBar)
hstack ships a macOS menu bar plugin powered by SwiftBar.
SwiftBar runs a script on an interval and renders its output as native macOS menu items.
Features
- Status at a glance with dynamic icons (green/orange/red)
- Server health
- Daemon status (PID + optional control server probe)
- Autostart LaunchAgent status
- Tailscale Serve status / URL (if configured)
- Quick controls
- Start / stop / restart the stack
- Restart just the daemon (stack-safe)
- Install / enable / disable / uninstall autostart
- Enable / disable Tailscale Serve
- Details
- PID, CPU %, RAM MB, uptime (where available)
- Useful URLs and file paths
- Stack details include aggregate CPU/RAM (server+daemon+autostart) when running
- Open logs in Console.app
- Refresh control
- Manual refresh
- In-menu refresh interval toggles (includes slower intervals like 10m/15m/30m/1h/6h/12h/1d)
- Uses a small helper script (
extras/swiftbar/set-interval.sh) to avoid SwiftBar quoting issues
- Stacks + services layout
- Main stack is shown directly (no extra nesting level)
- Each stack shows service rows (Server/Daemon/Autostart/Tailscale) with per-service submenus
- Components (git/worktrees)
- Available under a top-level Components submenu (to keep the main menu clean)
- Shows repo/worktree status for the active monorepo checkout (
<workspace>/main,<workspace>/dev, or a worktree under<workspace>/{pr,local,tmp}/...) - Each repo entry includes a Worktrees submenu listing all worktrees, with actions to switch/open
- Quick actions:
wt status/sync/update, PR worktree prompt, open shells/editors (wt shell/code/cursor) - Shows origin and upstream comparisons for the repo’s main branch (based on your last
git fetch) - Uses a Git cache by default so SwiftBar refresh stays fast even with many stacks/worktrees
Modes: selfhost vs dev
The menu supports two modes:
- Selfhost mode (
selfhost): lightweight “control panel” for running Happier.- Shows only the main stack essentials (Server/Daemon/Autostart/Tailscale) plus a small Maintenance section.
- Hides developer-oriented sections like stacks enumeration, git/worktrees, and worktree tooling.
- Dev mode (
dev): full stack control plane (stacks + Components (git/worktrees)).
How to switch modes
- In the menu, use the Mode section at the top, or
- From a terminal:
hstack menubar mode selfhost
hstack menubar mode devStacks (multiple instances)
If you create additional stacks (see docs/stacks.md), the plugin shows:
- Main stack (the default, stack name
main) - Stacks section listing each stack found under
~/.happier/stacks/<name>/env
Each stack row renders the same “mini control panel” (server/daemon/autostart/logs + a few actions) with stack-specific ports, dirs, and LaunchAgent label.
The menu also includes:
stack new --interactive(create stacks)stack edit <name> --interactive(edit stack port/server flavor/repo checkout)stack wt <name> -- use --interactive(switch the stack’s repo checkout/worktree)- “PR worktree into this stack (prompt)” (creates
wt pr ... --usescoped to the stack env)
Worktrees (quick entry points)
The menu also provides “jump off” actions for the worktree tooling:
hstack wt use --interactivehstack wt new --interactivehstack wt sync-allhstack wt update-all --dry-run/hstack wt update-allhstack wt pr ...(via an in-menu prompt)
For stack-specific worktree selection (which worktree a stack uses), use:
hstack stack edit <name> --interactive- or
hstack stack wt <name> -- use --interactive
- or
Note (worktree switching)
Happier is a single monorepo, so switching worktrees is always a repo-level action. Use:
hstack stack wt <name> -- use --interactive
Implementation notes
- Entry script:
extras/swiftbar/hstack.5s.sh(installed into SwiftBar ashstack.<interval>.sh) - Shared functions:
extras/swiftbar/lib/*.sh(sourced by the entry script) - Helper scripts:
extras/swiftbar/set-interval.shextras/swiftbar/set-server-flavor.sh
Install
1) Install SwiftBar
brew install --cask swiftbar2) Install the plugin
hstack menubar installIf you want a different default refresh interval at install time:
HAPPIER_STACK_SWIFTBAR_INTERVAL=15m hstack menubar install3) Open the active SwiftBar plugin folder
SwiftBar can be configured to use a custom plugin directory. To open the active one:
hstack menubar openUninstall
Remove the installed SwiftBar plugin files (does not delete your stacks/workspace):
hstack menubar uninstallHow refresh works (important)
SwiftBar’s refresh interval is controlled by the filename suffix:
hstack.30s.sh→ every 30 secondshstack.5m.sh→ every 5 minuteshstack.1h.sh→ every 1 hour
The plugin defaults to a slower interval (recommended), and also sets:
refreshOnOpen=false(recommended) to avoid surprise refreshes while you’re navigating the menu.
You can also change the interval directly from the menu via Refresh interval (it renames the plugin file and restarts SwiftBar).
Git cache (important for performance)
Git/worktree inspection is the most expensive part of the menu when you have many stacks. By default, the plugin runs in cached mode:
- It renders git/worktree info from an on-disk cache under
~/.happier-stack/cache/swiftbar/git. - Normal menu refreshes do not run git commands (so refresh stays snappy).
- The cache is refreshed explicitly (via menu actions), and can optionally refresh on TTL expiry.
Controls and settings:
- Refresh now: open Components → Git cache and run:
- “Refresh now (main components)”
- “Refresh now (all stacks/components)”
- or “Refresh now (this stack)” from a stack’s Components menu
- TTL:
HAPPIER_STACK_SWIFTBAR_GIT_TTL_SEC(default21600seconds = 6 hours) - Mode:
HAPPIER_STACK_SWIFTBAR_GIT_MODE=cached|live(defaultcached) - (Optional) Background auto-refresh:
HAPPIER_STACK_SWIFTBAR_GIT_AUTO_REFRESH_SCOPE=main|all|off(defaultmain)
Notes:
- Cached git info can be stale; it’s meant for at-a-glance signal.
- Actions like worktree switching/build/dev are always live (they use
hstack); only displayed git status is cached.
Maintenance (selfhost mode)
In selfhost mode, the menu includes a Maintenance section that can:
- show whether a
hstackupdate is available (from cached~/.happier-stack/cache/update.json) - run:
hstack self checkhstack self update
Terminal preference for interactive actions
Many menu actions open a terminal (interactive wizards, long-running dev servers, etc).
The plugin uses helper scripts so these run in your preferred terminal, using the same env var as wt shell:
HAPPIER_STACK_WT_TERMINAL=auto|ghostty|iterm|terminal|current
Notes:
autotries ghostty → iTerm → Terminal → current.- Ghostty is best-effort; if your Ghostty build can’t execute the command automatically, the command is copied to your clipboard and Ghostty is opened in the repo directory.
Start SwiftBar at login (optional)
SwiftBar is independent from the hstack LaunchAgent/service.
- In SwiftBar Preferences, enable Launch at Login, or
- Add SwiftBar to macOS Login Items.
Troubleshooting
Plugin doesn’t show up
- Ensure SwiftBar is running.
- Check which plugin folder SwiftBar is using:
- SwiftBar → Preferences → Plugin Folder
- Open the active folder:
hstack menubar open
Daemon shows “auth required” / “no machine”
This happens on a fresh machine (or any new stack) when the daemon does not yet have credentials in the stack-specific CLI home directory.
What’s going on
- The daemon stores credentials in
access.keyunder the CLI home directory. - For stacks (including main), that’s typically:
~/.happier/stacks/<name>/cli/access.key
- When
access.keyis missing, the daemon enters an interactive auth flow and won’t become a “machine” until it completes.- Under
launchd(autostart), this shows up as no machine and the daemon may appear “stopped”.
- Under
If it still needs auth
- In SwiftBar, open the Daemon section:
- If it shows
auth_required, click Auth login (opens browser)
- If it shows
- Or run manually:
hstack auth login“Daemon stale” even though it’s running
The plugin checks:
daemon.state.jsonPID is alive, and- (optionally) the daemon control server responds.
If the daemon is running but the menu is stale, refresh and check the PID line under “Daemon”.