tmux2kitty — Terminal emulator integration with tmux

Terminal emulator that natively works with tmux or GNU Screen, tmux2kitty brings tmux’s persistent remote sessions together with Kitty’s native tabs, keeping your local Kitty tabs synchronized with tmux windows running on a remote host. In other words, it provides smart tmux support with native tabs while allowing your remote terminal sessions and processes to persist across SSH disconnections.

If you use tmux or GNU screen together with Kitty, there is an interesting tension between the two programs.

tmux provides persistent terminal sessions, windows, and processes. This is especially useful when working over SSH because a tmux session can continue running on the remote machine even after the network connection disappears. You can reconnect later and continue where you left off.

Kitty, on the other hand, has a very nice native tab interface.

tmux2kitty is a small open-source project that connects these two worlds. It makes each tmux window appear as a native Kitty tab, while leaving tmux responsible for the actual persistent terminal sessions.

The project is available on GitHub.com/rambkk/tmux2kitty :

The basic idea

Normally, if you start tmux inside Kitty, you effectively have two layers of terminal management:

Kitty
└── one terminal window
    └── tmux
        ├── window 0
        ├── window 1
        ├── window 2
        └── window 3

tmux2kitty changes the presentation:

Kitty
├── Tab 0 — shell
├── Tab 1 — editor
├── Tab 2 — server
└── Tab 3 — logs
        │
        └── persistent tmux session

The important distinction is that the Kitty tabs are only the user interface. The actual sessions and processes continue to belong to tmux.

This is particularly useful for remote development. The tmux server can live on a remote machine while Kitty provides the local graphical tab interface.

What tmux2kitty does

The project currently supports both local and remote tmux sessions.

Its main functions are:

  • Attach to an existing tmux session and create a Kitty tab for each tmux window.
  • Create a tmux session when necessary.
  • Keep Kitty tabs synchronized with tmux windows.
  • Reflect tmux window numbers and names in Kitty tab titles.
  • Detect newly created and closed tmux windows.
  • Work over SSH.
  • Attempt to survive temporary network interruptions and reconnect.
  • Use Kitty’s remote-control interface rather than trying to emulate a terminal multiplexer itself.

The implementation is deliberately lightweight. The main program is a Bash script, with jq used for processing Kitty’s JSON state and a small Python helper used for tab ordering.

Why not just use Kitty tabs?

Kitty already has excellent native tabs, so why use tmux at all?

The answer is persistence.

A Kitty tab is primarily part of the local terminal UI. tmux, however, maintains a server-side session containing the windows and processes.

For example, suppose I SSH into a development server and start:

tmux
  ├── editor
  ├── shell
  ├── application
  └── logs

I can disconnect from SSH and the tmux session continues running.

Later, I reconnect and tmux2kitty can reconstruct the graphical representation:

Local Kitty
    │
    │ SSH
    ▼
Remote tmux server
    ├── editor
    ├── shell
    ├── application
    └── logs

The Kitty tabs therefore become a local view of a persistent remote tmux session.

How it works internally

The interesting part of tmux2kitty is that it does not try to integrate deeply with either program.

Instead, it acts as a small synchronization layer between their command-line interfaces.

There are essentially three components:

             ┌─────────────────┐
             │      Kitty      │
             │ native tabs/UI  │
             └────────┬────────┘
                      │
                 kitty @
                      │
             ┌────────▼────────┐
             │   tmux2kitty    │
             │     Bash        │
             └────────┬────────┘
                      │
                  SSH / tmux
                      │
             ┌────────▼────────┐
             │      tmux       │
             │ persistent      │
             │ sessions        │
             └─────────────────┘

1. Establishing the SSH connection

For remote sessions, tmux2kitty creates an SSH control connection rather than opening a completely independent SSH connection for every operation.

The script uses SSH options including:

ControlMaster=auto
ControlPath=...
ControlPersist=10m
ServerAliveInterval=10
ServerAliveCountMax=3
ConnectTimeout=10

This allows the various SSH commands used by the script to share a connection. It also gives the program a way to detect and recover from stale SSH control sockets.

For local operation, the same abstraction is retained but commands are executed locally instead of through SSH.

In other words, the rest of the script can effectively use:

ssh_remote "tmux ..."

without needing to care whether the target is local or remote.

2. Discovering tmux sessions

The script first queries tmux for its sessions and windows.

Conceptually it asks tmux for information equivalent to:

tmux list-sessions
tmux list-windows

For each window it obtains information such as:

window ID
window index
window name

The implementation eventually reduces this to records in the form:

window_id | window_index | window_name

That information becomes the mapping between tmux and Kitty.

The tmux window ID is particularly useful because window numbers can change when windows are reordered or closed. The internal reference provides a more stable way of identifying the corresponding Kitty tab.

3. Creating a Kitty tab

Kitty provides a remote-control interface through the kitty @ command.

tmux2kitty starts Kitty with remote control enabled and gives it a private Unix socket:

kitty
  --listen-on unix:<socket>

The script then wraps Kitty commands through a helper function conceptually equivalent to:

kitty @ --to unix:<socket> ...

This allows the Bash program to create and manipulate tabs without having to interact with Kitty’s terminal screen directly.

When a tmux window is turned into a Kitty tab, the script launches a new Kitty tab and associates the tmux window reference with it.

The resulting relationship is approximately:

tmux window
    │
    │ window ID
    ▼
Kitty tab
    │
    └── command line contains the window reference

That reference is important later when the script needs to find the correct Kitty tab again.

4. Mapping Kitty tabs back to tmux windows

Kitty’s remote-control ls command returns information about the current Kitty windows and tabs as JSON.

This is where jq becomes useful.

tmux2kitty examines the Kitty state and searches the command-line information associated with each tab for the tmux window reference.

Conceptually:

Kitty JSON
    │
    ├── Tab A
    │     └── tmux window @0
    │
    ├── Tab B
    │     └── tmux window @1
    │
    └── Tab C
          └── tmux window @2

Once the matching Kitty tab ID has been found, the script can focus that tab or change its title.

This is a clever aspect of the design: the mapping does not require a separate database of tmux-to-Kitty tab IDs. The association can be rediscovered from Kitty’s own state.

5. Keeping tab names synchronized

A tmux window might be called:

0-shell
1-editor
2-server

tmux2kitty uses the tmux window index and name to construct a corresponding Kitty title.

For example:

tmux window 1
name: editor

        ↓

Kitty tab title:

1-editor

If the tmux window is renamed, the script can find the corresponding Kitty tab and update both the tab and window titles.

This makes the Kitty interface effectively mirror the tmux window list.

6. Keeping the ordering correct

One subtle problem is that creating and destroying tabs does not necessarily guarantee that Kitty’s physical tab order matches tmux’s window order.

tmux might contain:

0 shell
1 editor
2 logs
3 server

while Kitty could temporarily have:

0 shell
2 logs
1 editor
3 server

tmux2kitty includes a small Python helper specifically for dealing with this.

It queries Kitty’s tab state, identifies tabs whose titles begin with numeric window indexes, calculates the desired ordering, and moves tabs until the numbered tabs correspond to tmux’s ordering. Unnumbered tabs are deliberately left alone.

So the Python component isn’t implementing the main application logic. It solves a very specific piece of UI synchronization.

7. Handling new and removed tmux windows

The synchronization loop periodically compares the current tmux window state with the known Kitty tabs.

That makes it possible to handle changes such as:

tmux:
    create-window
         │
         ▼
tmux2kitty notices new window
         │
         ▼
Kitty tab created

Likewise:

tmux:
    kill-window
         │
         ▼
tmux2kitty notices missing window
         │
         ▼
corresponding Kitty tab removed

The result is that you can continue using normal tmux commands while Kitty’s native tab bar follows along.

Remote reconnection

The remote case is where the design becomes particularly useful.

The project configures SSH keepalive behavior and maintains an SSH control connection. If the connection disappears, the script can retry the connection rather than immediately abandoning the session.

The important thing to understand is that the tmux session itself does not depend on the SSH connection remaining alive.

If the network disappears:

Kitty ───X─── SSH ─── Remote tmux
                         │
                         ├── editor
                         ├── shell
                         └── server

the remote tmux server continues running.

When connectivity is restored, tmux2kitty can reconnect to the existing session and restore the Kitty-side representation.

A useful mental model

I think the easiest way to understand tmux2kitty is to separate the responsibilities:

ComponentResponsibility
KittyTerminal emulator and graphical tab UI
tmuxPersistent sessions, windows and processes
SSHTransport to the remote machine
tmux2kittySynchronization between the two UIs
jqParse Kitty’s JSON state
Python helperSort Kitty tabs and update active window

tmux2kitty is therefore not really a replacement for tmux or Kitty.

It is an adapter.

             User interface
                   │
                   ▼
              ┌─────────┐
              │  Kitty  │
              └────┬────┘
                   │
             tmux2kitty
                   │
              ┌────▼────┐
              │  tmux   │
              └────┬────┘
                   │
             persistent
               process

That separation is what makes the project interesting.

Installation

The project has very few requirements:

  • Kitty with remote-control support
  • tmux
  • Bash
  • jq
  • Python 3
  • SSH when using remote sessions

The repository provides a simple installation flow:

git clone https://github.com/rambkk/tmux2kitty.git
cd tmux2kitty
chmod +x tmux2kitty

The README currently documents Ubuntu/Debian package installation for tmux, jq and Python, as well as Kitty.

Usage

A remote session can be started with:

./tmux2kitty user@server

or an existing tmux session can be selected explicitly:

./tmux2kitty user@server -t mysession

For local tmux:

./tmux2kitty local

or:

./tmux2kitty local -t dev-environment

These commands are documented by the project itself.

Why I find the project interesting

There is a broader idea here that is worth noticing.

Terminal applications increasingly have features that historically belonged to terminal multiplexers: tabs, splits, session management, remote workflows, keyboard handling and even graphical capabilities.

At the same time, tmux remains extremely useful because it provides a durable, server-side terminal environment.

The problem is that using both can sometimes mean duplicating the UI:

Kitty tab
    └── tmux window
          └── application

tmux2kitty takes a different approach:

Kitty tab
    │
    └── represents ──► tmux window
                           │
                           └── application

The terminal emulator becomes the graphical presentation layer while tmux remains the persistence layer.

That is a small idea, but it produces a very nice workflow for people who spend a lot of time in terminals, particularly when working on remote machines.

Project

tmux2kitty is open source and licensed under the MIT license.

The source code, README, configuration examples and current implementation are available here: github.com/rambkk/tmux2kitty

The repository is small enough that the implementation is also worth reading directly. The main tmux2kitty program is currently in Bash script, making it a relatively approachable example of using SSH, tmux, Kitty’s remote-control API, JSON processing and a small amount of Python to glue several command-line tools together.