I migrated my terminal multiplexer from tmux to zellij

Wait, "herdr" doesn't match any known terminal multiplexer. Let me provide a direct translation:

I migrated my terminal multiplexer from tmux to herdr

I migrated my terminal multiplexer from tmux to zellij Wait, "herdr" doesn't match any known terminal multiplexer. Let me provide a direct translation: I migrated my terminal multiplexer from tmux to herdr

I migrated my terminal multiplexer from tmux to herdr. This article introduces the steps for setting up herdr, which natively supports AI agent integration, along with actual configuration and integration methods, complete with implementation examples.
2026.07.07

This page has been translated by machine translation. View original

Introduction

I migrated my terminal multiplexer from tmux to herdr.

herdr is a terminal multiplexer specialized for running AI coding agents. Like tmux, it persists panes, tabs, and sessions, while natively displaying the state (idle / working / blocked / done) of agents running in each pane in a sidebar. It is implemented in Rust and the TUI library Ratatui. The official tagline is "one terminal for the whole herd," and it claims to be Electron-free, account-free, and telemetry-free.

This article describes the steps I took to replace my environment, which was built with tmux and the tmux-agent-sidebar plugin, with herdr. This is a record of actually introducing it into my own dotfiles.

Note that herdr is at version 0.7.1 at the time of writing this article. Behavior and configuration may change with future updates. Please check the official documentation for the latest information.

Configuration Before Migration

Before migrating, I used tmux as my multiplexer and hiroppy/tmux-agent-sidebar for displaying agent status.

This plugin calls a script from the agent's hooks and renders the state in a tmux sidebar. In my environment, I had it configured to work with Codex, calling hook.sh from ~/.codex/hooks.json.

The tmux side was configured as follows.

  • Plugin management: tpm (Tmux Plugin Manager)
  • Theme: catppuccin (mocha)
  • Status bar: battery / cpu / online-status / prefix-highlight
  • Session persistence: continuum / resurrect

Why Migrate?

The motivation for migrating was wanting to handle agent state display in a more natural way.

tmux-agent-sidebar is a mechanism that retrofits agent awareness onto tmux, a "tool designed before agents existed." herdr, on the other hand, places agent state display at its core from the start. It identifies processes running in panes as agents and displays their state in a sidebar with color-coded indicators.

Additionally, herdr has features such as mouse-native operation and remote attach via SSH. I judged it worth trying as an option suited to agent operations while leveraging tmux assets.

As a migration destination for my multiplexer, I also tried Zellij previously. However, it didn't fit my usage style, so I passed on it at the time.

The main reason was that I couldn't carry over the C-q prefix I was using in tmux. Zellij uses a modal keybinding approach rather than a single-prefix approach like tmux. By default, you enter modes with the Primary Modifier (Ctrl) and execute common operations with the Secondary Modifier (Alt) (see Zellij documentation).

Keybindings and modifier keys themselves can be customized in config.kdl. However, this is fundamentally different from the tmux-style design of "converging to a single prefix."

In that regard, herdr uses the same single-prefix approach as tmux. I was able to reproduce C-q as-is with a single line: prefix = "ctrl+q". This ease of migration was also one of the reasons I chose herdr.

Installation

herdr is registered as a Homebrew core formula. Since I manage my dotfiles with Homebrew, I installed it via brew.

$ brew install herdr

After installation, I verified the version.

$ herdr --version
herdr 0.7.1

I removed the tmux line from my dotfiles' .Brewfile and added herdr in its place.

brew "herdr"

Configuration

The herdr configuration file is ~/.config/herdr/config.toml. You can output the default configuration with the following command.

$ herdr --default-config

Looking at the output, the pane movement in [keys] was assigned by default to prefix+h / prefix+j / prefix+k / prefix+l. This matched the Vim-style keybindings I was using in tmux, so I could keep this part as-is.

I ported the main settings from the old .tmux.conf as follows.

tmux setting herdr setting
prefix C-q [keys] prefix = "ctrl+q"
Pane movement h/j/k/l Default focus_pane_left/down/up/right (prefix+h/j/k/l)
| for horizontal split split_vertical (default prefix+v)
- for vertical split split_horizontal (default prefix+minus)
bind g to launch lazygit [[keys.command]] to launch lazygit in a pane
catppuccin mocha [theme] name = "catppuccin"
default-shell /bin/zsh [terminal] default_shell = "/bin/zsh"
mouse on [ui] mouse_capture = true

I confirmed the split directions using the herdr CLI. herdr pane split --direction right places a new pane to the right (horizontal split), and --direction down places it below (vertical split). To preserve the correspondence between the old | (horizontal) and - (vertical), I mapped split_vertical as the | equivalent and split_horizontal as the - equivalent.

The lazygit launch key required attention. I was using prefix + g in tmux, but in herdr, prefix+g is already assigned by default to workspace navigation (goto). To avoid the conflict, I assigned it to prefix+alt+g following the official herdr example.

The final config.toml is as follows. Items that remain at their default values are omitted.

[theme]
name = "catppuccin"

[terminal]
default_shell = "/bin/zsh"

[keys]
prefix = "ctrl+q"

[[keys.command]]
key = "prefix+alt+g"
type = "pane"
command = "lazygit"

[ui]
mouse_capture = true

You can apply configuration changes without restarting the server using the following command.

$ herdr server reload-config
{"id":"cli:server:reload-config","result":{"diagnostics":[],"status":"applied","type":"config_reload"}}

If diagnostics is empty and status is applied, the configuration has been parsed successfully.

Claude Code Integration

herdr has built-in integration with major agents. Integration is set up with herdr integration install <agent>.

This time I integrated it with Claude Code.

$ herdr integration install claude
installed claude integration hook to /Users/<user>/.claude/hooks/herdr-agent-state.sh
ensured claude settings at /Users/<user>/.claude/settings.json

This command makes two changes. One is generating a hook script at ~/.claude/hooks/herdr-agent-state.sh. The other is appending a SessionStart hook to ~/.claude/settings.json.

The appended hook takes the following form.

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "command": "bash '/Users/<user>/.claude/hooks/herdr-agent-state.sh' session",
            "timeout": 10,
            "type": "command"
          }
        ],
        "matcher": "*"
      }
    ]
  }
}

You can check the integration status with herdr integration status.

$ herdr integration status
claude: current (v7) (/Users/<user>/.claude/hooks/herdr-agent-state.sh)

If current is displayed, the hook is up to date. When you launch Claude Code inside a herdr pane, the hook notifies herdr of session information, which is reflected in the sidebar's state display.

Here is a note for dotfiles managers. The hooks in the generated settings.json have an absolute path written as /Users/<user>/.claude/hooks/.... If you share dotfiles across multiple machines, be aware that the path includes the machine name. I placed the hook script under dotfiles management, and for settings.json, I adopted an approach of running herdr integration install claude on each machine during setup to keep things consistent.

Furthermore, herdr provides a mechanism for agents themselves to operate herdr. By loading the SKILL.md from the official repository into an agent, the agent can autonomously handle adjustments such as creating workspaces, tabs, and panes, as well as executing commands in other panes and collecting results, via the CLI and Unix socket API (this usage is covered in detail in this Zenn article). I placed this SKILL.md as a Claude Code user skill at ~/.claude/skills/herdr/SKILL.md (the actual file is under dotfiles management). Since ~/.claude is managed collectively with dotfiles along with the hooks and settings.json, herdr-related settings can all be handled in one place. herdr's SKILL.md already has frontmatter with name and description, so simply placing it under ~/.claude/skills/herdr/ is enough for Claude Code to recognize it as a skill.

Removing tmux

After confirming that herdr was working, I removed the tmux-related configuration.

  • Deleted .tmux.conf
  • Removed tmux from .Brewfile and uninstalled the tmux binary
  • Deleted Codex's hook configuration that was calling tmux-agent-sidebar (~/.codex/hooks.json and the marketplace registration in config.toml)
  • Deleted ~/.tmux/plugins (tpm and each plugin)

Don't forget to rewrite the terminal auto-launch as well. I was auto-attaching to tmux from Alacritty.

# Before
args = ["-l", "-c", "tmux a -t default || tmux new -s default"]

# After
args = ["-l", "-c", "herdr"]

The herdr command alone handles both attaching to an existing persistent session and creating a new session. It covers the behavior equivalent to tmux's attach || new with a single command.

I also had a zsh hook that changed the tmux session name to the repository name when navigating with ghq. Since herdr automatically labels workspaces from the working directory, this hook was no longer needed.

Thoughts After Migrating

Here is a summary of what I gained and lost from the migration.

I gained the following three things.

  • Native visualization of agent state. Rather than a plugin retrofit, herdr itself displays state in the sidebar.
  • Mouse operation. Panes and tabs can be handled with clicks and drags.
  • Remote attach. With herdr --remote <ssh-target>, you can attach to a session on another machine.

On the other hand, there are things I lost. The widgets displayed in tmux's status bar such as battery / cpu / online-status have no equivalent functionality in herdr. If you need this information, you will need to consider alternative means.

Regarding session persistence, the concept shifted from automatic restoration at startup via continuum / resurrect to persistence through herdr's resident server. As long as the herdr server keeps running, it retains engines and panes, and agents continue running even when a client is detached.

Conclusion

I introduced the migration from tmux to herdr.

herdr is a multiplexer that inherits tmux's assets of persistence and pane operations while placing agent state display at its core. For a development style where you routinely run multiple agents, I felt it was worth trying.

On the other hand, herdr is still a young tool. If you place importance on tmux's rich plugin ecosystem and fine-grained customization, tmux currently has the advantage. Please make your adoption decision after determining whether it fits your own usage style.

References

Share this article