# Menu bar (SwiftBar)

`hstack` ships a macOS menu bar plugin powered by [SwiftBar](https://swiftbar.app/).

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 + git/worktrees + worktree tooling).

### How to switch modes

- In the menu, use the **Mode** section at the top, or
- From a terminal:

```bash
hstack menubar mode selfhost
hstack menubar mode dev
```

## Stacks (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 ... --use` scoped to the stack env)

## Worktrees (quick entry points)

The menu also provides “jump off” actions for the worktree tooling:

- `hstack wt use --interactive`
- `hstack wt new --interactive`
- `hstack wt sync-all`
- `hstack wt update-all --dry-run` / `hstack wt update-all`
- `hstack 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`

### 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 as `hstack.<interval>.sh`)
- **Shared functions**: `extras/swiftbar/lib/*.sh` (sourced by the entry script)
- **Helper scripts**:
  - `extras/swiftbar/set-interval.sh`
  - `extras/swiftbar/set-server-flavor.sh`

## Install

### 1) Install SwiftBar

```bash
brew install --cask swiftbar
```

### 2) Install the plugin

```bash
hstack menubar install
```

If you want a different default refresh interval at install time:

```bash
HAPPIER_STACK_SWIFTBAR_INTERVAL=15m hstack menubar install
```

### 3) Open the active SwiftBar plugin folder

SwiftBar can be configured to use a custom plugin directory. To open the *active* one:

```bash
hstack menubar open
```

## Uninstall

Remove the installed SwiftBar plugin files (does not delete your stacks/workspace):

```bash
hstack menubar uninstall
```

## How refresh works (important)

SwiftBar’s refresh interval is controlled by the **filename** suffix:

- `hstack.30s.sh` → every 30 seconds
- `hstack.5m.sh` → every 5 minutes
- `hstack.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` (default `21600` seconds = 6 hours)
- **Mode**: `HAPPIER_STACK_SWIFTBAR_GIT_MODE=cached|live` (default `cached`)
- (Optional) **Background auto-refresh**: `HAPPIER_STACK_SWIFTBAR_GIT_AUTO_REFRESH_SCOPE=main|all|off` (default `main`)

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 `hstack` update is available (from cached `~/.happier-stack/cache/update.json`)
- run:
  - `hstack self check`
  - `hstack 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:
- `auto` tries 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.key` under the CLI home directory.
- For stacks (including main), that’s typically:
  - `~/.happier/stacks/<name>/cli/access.key`
- When `access.key` is missing, `happy-cli daemon start` 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”.

**If it still needs auth**

- In SwiftBar, open the **Daemon** section:
  - If it shows `auth_required`, click **Auth login (opens browser)**
- Or run manually:

```bash
hstack auth login
```

### “Daemon stale” even though it’s running

The plugin checks:

- `daemon.state.json` **PID 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”.
