Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

herdr-focus

(日本語)

A herdr plugin that focuses the next agent pane in blocked (needs attention) or done (finished, unread) state, and brings the hosting terminal app to the front. If no pane needs attention, it still brings the terminal app to the front. An optional global hotkey (default Ctrl+Shift+O) triggers it from any app.

demo: one keypress jumps to the blocked agent, approve it, next keypress jumps to the finished one

Inspired by a post from Tatsuhiko Miyagawa (@miyagawa). The core logic here — cycling through blocked/done panes by priority — follows the same idea as the herdr-unread script he had bound to prefix+shift+u.

Install

herdr plugin install trapple/herdr-focus

That's it. No build step. Invoke the focus action from a terminal:

herdr plugin action invoke trapple.herdr-focus.focus

Priority is blocked > done. Repeated invocations cycle to the next pane within the same group.

Or bind it to a key inside herdr (herdr config edit):

[[keys.command]]
key = "prefix+o"
type = "plugin_action"
command = "trapple.herdr-focus.focus"
description = "focus next agent pane needing attention"

Global hotkey (optional)

To trigger the focus action from any app with a system-wide hotkey, install the resident daemon. This step (and only this step) requires Xcode Command Line Tools (swiftc):

herdr plugin action invoke trapple.herdr-focus.hotkey-install

This opens an overlay pane inside herdr that streams the build and install progress live (press Enter to close it when it finishes — errors stay on screen until you do). It compiles the daemon into the plugin's state directory and registers a LaunchAgent (com.trapple.herdr-focus-hotkeyd), so it auto-starts at login and launchd restarts it if it crashes. The default hotkey is Ctrl+Shift+O.

The daemon captures the hotkey via Carbon's RegisterEventHotKey API, which doesn't require Accessibility permission. The first time the hotkey fires, macOS may show a one-time prompt asking whether "herdr-focus-hotkeyd" wants to control "Ghostty" (or whatever terminal app is configured). Allow it.

To remove the daemon (do this before herdr plugin uninstall):

herdr plugin action invoke trapple.herdr-focus.hotkey-uninstall

Configuration

Settings live in a config file inside the plugin's config directory:

$(herdr plugin config-dir trapple.herdr-focus)/config

hotkey-install creates it with defaults. If you don't use the hotkey and only want to change the terminal app, create it yourself:

TERMINAL_APP="Ghostty"
HOTKEY="ctrl+shift+o"
  • TERMINAL_APP: the app hosting herdr, brought to the front after focusing. Use the app name as AppleScript sees it (e.g. Terminal, iTerm2). Takes effect immediately — no re-registration needed. The HERDR_FOCUS_TERMINAL_APP environment variable, when set, overrides this.
  • HOTKEY: the global hotkey. Modifiers: ctrl / shift / alt / cmd; key: a-z, 0-9, f1-f12 (e.g. ctrl+shift+o, cmd+alt+f6). After changing it, re-run hotkey-install to re-register the daemon:
herdr plugin action invoke trapple.herdr-focus.hotkey-install

How it works

herdr-plugin.toml              # Plugin manifest: declares the three actions below
bin/
├── focus                      # Core logic (bash). Cycles blocked/done panes, activates the terminal app
├── hotkey-install             # Builds the daemon into the state dir, validates config, registers the LaunchAgent
└── hotkey-uninstall           # Removes the LaunchAgent and the built daemon
src/herdr-focus-hotkeyd.swift  # Global hotkey daemon source (compiled on demand by hotkey-install)
build.sh                       # swiftc build script (output dir given as argument)

Every trigger path goes through the same plugin action, so there's no duplicated logic to maintain:

global hotkey → hotkeyd → herdr plugin action invoke trapple.herdr-focus.focus
herdr keybinding (plugin_action) ─────────────────────→ same action
manual CLI invoke ────────────────────────────────────→ same action

Because the action always runs inside herdr's plugin environment, bin/focus reads the herdr binary from HERDR_BIN_PATH and the config from HERDR_PLUGIN_CONFIG_DIR — no hardcoded paths.

After reinstalling the plugin (e.g. to update), re-run hotkey-install to rebuild and refresh the daemon.

Requirements

  • herdr >= 0.7.0
  • jq
  • macOS (uses osascript and Carbon)
  • Xcode Command Line Tools (swiftc) — only if you use the global hotkey

Migrating from the pre-plugin version

Earlier versions installed via ./install.sh into ~/bin with a LaunchAgent. To migrate:

  1. herdr plugin install trapple/herdr-focus
  2. If you use the global hotkey: herdr plugin action invoke trapple.herdr-focus.hotkey-install — the LaunchAgent label is unchanged, so this replaces the old daemon automatically. If you don't: launchctl bootout gui/$(id -u)/com.trapple.herdr-focus-hotkeyd and delete ~/Library/LaunchAgents/com.trapple.herdr-focus-hotkeyd.plist
  3. Remove the old binaries: rm ~/bin/herdr-focus ~/bin/herdr-focus-hotkeyd
  4. If you exported HERDR_FOCUS_TERMINAL_APP in your shell profile, remove it and set TERMINAL_APP in the config file instead

Troubleshooting

plugin action invoke starts the action and prints a command log record; the action's own output goes to the plugin log, not your terminal (hotkey-install is the exception — it streams its output in an overlay pane). Check the log with:

herdr plugin log list --plugin trapple.herdr-focus

Development

git clone https://github.com/trapple/herdr-focus
cd herdr-focus
herdr plugin link "$PWD"

Manual verification checklist:

  1. herdr plugin action list --plugin trapple.herdr-focus shows the three actions
  2. herdr plugin action invoke trapple.herdr-focus.focus cycles through blocked/done panes — works without any build (the hotkey really is optional)
  3. herdr plugin action invoke trapple.herdr-focus.hotkey-install opens an overlay pane streaming the build, then the hotkey fires
  4. Editing TERMINAL_APP in the config takes effect on the next invocation without re-registering
  5. Editing HOTKEY + re-running hotkey-install switches the hotkey
  6. herdr plugin action invoke trapple.herdr-focus.hotkey-uninstall removes the daemon and the hotkey stops firing

Acknowledgements

Thanks to Tatsuhiko Miyagawa (@miyagawa) for the original idea and script that inspired this project.

About

Focus the next blocked/done agent pane and bring the terminal app to front. Global hotkey included.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages