Herdling is a macOS menu bar companion for Herdr. It shows local and SSH-hosted Herdr agents, keeps their status current, and opens the selected session in Ghostty or Kero.
- Groups agents by machine, session, Space, and worktree while preserving Herdr order.
- Activity prioritizes agents by status: blocked, done, working, then the three most recently updated idle agents. Sources opens the full directory; expand one source or keep them all closed.
- Keeps rows in place while browsing, with live status; reopen to rebuild the list. Failed jumps retain their target for retry.
- Updates local and remote sessions through Herdr's socket event stream, with discovery and CLI fallback where needed.
- Choose Ghostty or Kero in Settings; the choice is saved. Reuses a matching Herdr client in that terminal, or creates and verifies a new one. Ghostty keeps its Tab/Window setting; Kero opens new sessions.
- Reads SSH hosts from
~/.ssh/config, includingIncludefiles. - Supports Launch at Login, keyboard navigation, VoiceOver labels, light/dark appearance, and Reduce Motion.
- macOS 15 or later
- Xcode 26.2 or later with Swift 6.2 or later (only when building from source; Command Line Tools alone are not sufficient)
- Herdr installed locally and on each enabled SSH host
- Ghostty 1.4.0 or later, or Kero with its
keroCLI for opening and focusing sessions - Key-based or otherwise non-interactive SSH access for remote hosts
Herdling finds herdr through HERDR_BIN_PATH or common Homebrew/system paths. It finds kero through KERO_BIN_PATH, common Homebrew paths, or the Kero app bundle. Remote commands run through the host's login shell.
Check Releases for binary builds: choose arm64 for Apple Silicon or x86_64 for Intel. Extract Herdling.app and move it to /Applications. Each ZIP has a .sha256 file; verify it with shasum -a 256 -c <filename>.zip.sha256 from the download directory.
These builds use ad-hoc signing, without an Apple Developer ID certificate or notarization. macOS may block the first launch. After attempting to open the app, use System Settings → Privacy & Security → Open Anyway only if you trust the download. Do not disable Gatekeeper system-wide.
To build from source:
git clone https://github.com/JinJieBeWater/herdling.git
cd herdling
bash scripts/build-app.sh release
open .build/Herdling.appThe script creates an ad-hoc-signed app at .build/Herdling.app. Move it to a stable location such as /Applications before enabling Launch at Login.
For a development build:
bash scripts/build-app.sh
open .build/Herdling.app- Start at least one Herdr session.
- Open Herdling from the menu bar.
- Open Settings with
⌘,or by right-clicking the menu bar item. - Enable required aliases under SSH Sources.
- Under Terminal → Application, choose Ghostty or Kero. The default remains Ghostty; switching only affects subsequent jumps, not existing terminal sessions.
Ghostty uses AppleScript Automation; allow Herdling to control Ghostty when macOS asks, keep macos-applescript enabled (the default), and choose Tab or Window for new clients. Kero uses its native CLI, with no Automation permission or Tab/Window setting. Settings checks only the selected terminal.
Herdling uses named Host entries from ~/.ssh/config. Wildcard and negated entries are ignored. Each enabled host must:
- connect without an interactive password prompt;
- expose
herdrthrough its login shell; - provide
ncwith Unix-domain socket support.
Check the connection before enabling it:
ssh -o BatchMode=yes my-server '$SHELL -lc "herdr session list --json"'| Action | Result |
|---|---|
| Left-click menu bar item | Open or close roster |
| Right-click menu bar item | Open Settings/Quit menu |
| Click a machine in Sources | Toggle its directory; expanding one closes the previous source |
| Click an agent, worktree, Space, or session | Focus it in the selected terminal |
↑ / ↓ |
Select a row without navigating |
Enter |
Open selected target; source rows only toggle their directory |
← / → in Sources |
Return to parent / collapse or expand a source |
Tab / Shift-Tab |
Move among native controls |
⌘, |
Open Settings |
⌘W in Settings |
Close the Settings window |
Esc in roster |
Close panel |
⌘Q |
Quit Herdling |
Herdr focus can mark every pane in the destination tab as seen and affect all shell clients in that session. A failed jump may already have changed focus; it does not imply rollback or that a task was handled. Recent idle rows come from Herdr's state timestamps, not navigation history.
Run tests:
bash scripts/test-build-app.sh
swift test --no-parallelBuild release app:
bash scripts/build-app.sh release.github/workflows/release.yml tests, builds and verifies app packaging for Apple Silicon and Intel on pushes to main, pull requests to main, and manual runs. These runs upload ZIPs and SHA-256 files as Actions artifacts without publishing a release. Xcode 26.2 is selected explicitly for Swift 6.2 and the macOS 26 APIs.
CI runs the complete automated suite: navigation, session state, process/socket failures and cancellation, preferences, and packaging. Pixel and OCR checks are not part of the suite. Check visual changes and real terminal navigation manually; use an isolated desktop for GUI automation.
To publish, set CFBundleShortVersionString in Resources/Info.plist to the release version and increment CFBundleVersion for subsequent releases. Commit and push the changes, including the workflow, then tag that commit:
git tag -a v0.1.0 -m 'Herdling 0.1.0'
git push origin v0.1.0Use v<CFBundleShortVersionString> for the tag; a mismatch fails the build. After both architectures pass, the workflow creates a GitHub Release with both ZIPs, checksums and generated release notes. It only needs the automatic GITHUB_TOKEN; no Apple certificates or repository secrets are required. Do not create the Release manually first or move an existing release tag; use a new version for corrections.
- AppKit owns
NSStatusItemand a nonactivatingNSPanel; SwiftUI renders panel content. - Local and remote monitors maintain one NDJSON socket stream per Herdr session.
- Periodic discovery finds added or removed sessions. CLI polling remains a fallback when a stream is unavailable.
- Ghostty matches its native AppleScript
ttyto the Herdr client process, then focuses the stable terminal ID. It does not change terminal titles or save terminal mappings. Kero useskero ls --json, pane process ancestry andkero focus; new clients usekero new. Each backend verifies its own client, without falling back to another terminal.
- No local sessions: confirm
herdr session list --jsonworks in a new shell. SetHERDR_BIN_PATHif Herdr is installed elsewhere. - SSH source stays offline: run the BatchMode command above. Fix host keys, authentication, remote
PATH, or missingncbefore retrying. - Kero does not open or focus: open Kero and check
kero ls --json. SetKERO_BIN_PATHif Kero is installed elsewhere. If a new client fails to start, check Herdr and SSH before retrying; failed sessions are not automatically deleted. - Ghostty does not open or focus: check System Settings → Privacy & Security → Automation and allow Herdling to control Ghostty.
- Launch at Login is unavailable: run Herdling from
.build/Herdling.appor another app bundle, not throughswift run.
Herdling itself does not currently declare a software license. The bundled Herdr mark is covered by third-party notices and the bundled Apache License 2.0 text.