zmx

repository·main·Indexed 23 days ago

https://github.com/neurosnap/zmx

A terminal session management tool designed for session persistence (attach/detach) without the complexity of windows, tabs, or splits. zmx handles the shell lifecycle while letting the OS window manager handle layout. It provides a CLI to attach to interactive shells, run background commands in a PTY, send raw input to sessions, and manage session metadata via labels.

Tokens
3.5K
Snippets
8
Records
20
Agent score
34%

What's inside zmx

  1. Use ZMX_SESSION_PREFIX to simplify commands

    main

    You can set the ZMX_SESSION_PREFIX environment variable to prefix all commands that accept a session name. This is useful for grouping related tasks.

    export ZMX_SESSION_PREFIX="d."
    zmx a runner # Creates/attaches to session 'd.runner'
    zmx a tests  # Creates/attaches to session 'd.tests'
    zmx k tests  # Kills session 'd.tests'
    zmx wait     # Waits for all tasks prefixed with 'd.' to complete
    export ZMX_SESSION_PREFIX="d."
    zmx a runner
  2. Configure shell prompts for zmx sessions

    main

    When inside a zmx session, the environment variable ZMX_SESSION is set to the session name. Since zmx does not provide visual indicators of being inside a session, it is recommended to update your shell prompt to display the session name.

    Fish

    Add to ~/.config/fish/config.fish:

    functions -c fish_prompt _original_fish_prompt 2>/dev/null
    
    function fish_prompt --description 'Write out the prompt'
      if set -q ZMX_SESSION
        echo -n "[$ZMX_SESSION] "
      end
      _original_fish_prompt
    end

    Bash and Zsh

    Add to .bashrc or .zshrc:

    if [[ -n $ZMX_SESSION ]]; then
      export PS1="[$ZMX_SESSION] ${PS1}"
    fi

    Powerlevel10k (Zsh)

    Add to .zshrc:

    function prompt_my_zmx_session() {
      if [[ -n $ZMX_SESSION ]]; then
        p10k segment -b '%k' -f '%f' -t "[$ZMX_SESSION]"
      fi
    }
    POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS+=my_zmx_session

    Oh My Posh

    Add to your configuration:

    [[blocks.segments]]
       template = '{{ if .Env.ZMX_SESSION }} {{ .Env.ZMX_SESSION }}{{ end }}'
       foreground = 'p:orange'
       background = 'p:black'
       type = 'text'
       style = 'plain'

    Starship

    Add to starship.toml:

    format = """
    ${env_var.ZMX_SESSION}\n..."
    """
    
    [env_var.ZMX_SESSION]
    symbol = " "
    format = "[$symbol$env_value]($style) "
    description = "zmx session name"
    style = "bold magenta"
  3. Configure SSH for zmx multiplexing

    main

    To enable a workflow where multiple terminal windows attach to different zmx sessions on a single remote server via a single connection, add a configuration entry to your SSH config file.

    Key settings:

    • RemoteCommand zmx attach %k: Automatically runs the zmx attach command using the host alias as the session name.
    • RequestTTY yes: Ensures the TTY is allocated (replaces the need for -t).
    • ControlMaster auto and ControlPath: Enables SSH multiplexing, allowing multiple sessions to share one TCP connection.
    • ControlPersist: Keeps the master connection alive in the background.
    Host = d.*
        HostName 192.168.1.xxx
    
        RemoteCommand zmx attach %k
        RequestTTY yes
        ControlPath ~/.ssh/cm-%r@%h:%p
        ControlMaster auto
        ControlPersist 10m
  4. Install zmx

    main

    You can install zmx using several methods depending on your operating system and preferred package manager:

    Homebrew (macOS)

    brew install neurosnap/tap/zmx

    mise

    mise use zmx

    Nix / NixOS

    Run without installation:

    nix run github:NixOS/nixpkgs/nixpkgs-unstable#zmx

    Start a shell with zmx available:

    nix shell github:NixOS/nixpkgs/nixpkgs-unstable#zmx

    Build from source

    Requires Zig v0.16. Clone the repository and run:

    zig build -Doptimize=ReleaseSafe --prefix ~/.local

    Note: Ensure ~/.local/bin is in your PATH after building.

    Binaries

    Direct downloads are available for Linux (aarch64, x86_64) and macOS (aarch64, x86_64) at https://zmx.sh/a/.

    brew install neurosnap/tap/zmx
  5. Use zmx with SSH

    main

    To use zmx over SSH without modifying your SSH configuration, you must pass the -t flag to ensure the remote shell recognizes it is talking to a terminal. Without this, the display will likely malfunction.

    Quick trial:

    ssh -t dev-box zmx attach default

    Recommended Configuration: For a seamless workflow, configure your ~/.ssh/config to automatically handle TTY requests and multiplexing. This allows you to open multiple native terminal windows and run ssh for each, effectively using zmx as a replacement for tmux panes while retaining native terminal features (scrollback, splits, etc.).

    Use ControlMaster auto and RequestTTY yes to enable multiplexing and automatic TTY allocation.

  6. Enable shell completions for zmx

    main

    You can enable auto-completion for zmx commands and session names. Note that if you installed via homebrew, completions are installed automatically.

    Bash

    Add to .bashrc:

    if command -v zmx &> /dev/null; then
      eval "$(zmx completions bash)"
    fi

    Zsh

    Add to .zshrc:

    if command -v zmx &> /dev/null; then
      eval "$(zmx completions zsh)"
    fi

    Fish

    Add to ~/.config/fish/completions/zmx.fish:

    if type -q zmx
      zmx completions fish | source
    end
    eval "$(zmx completions zsh)"
  7. Use zmx to manage persistent terminal sessions

    main
    zmx is a session persistence tool for terminal processes. It allows you to run commands in a PTY (Pseudo-Terminal), detach from them, and re-attach later, or run tasks in the background and track their status. It supports attaching to interactive shells, running non-interactive commands, and sending raw input or injecting text into session displays.
  8. Configure socket and log directory permissions

    main

    You can control the filesystem permissions for the directories where zmx stores sockets and logs using these environment variables. This is useful for shared group access (e.g., when running zmx as a system service).

    • ZMX_DIR_MODE: Sets the mode for the socket and log directories (octal, defaults to 0750).
    • ZMX_LOG_MODE: Sets the mode for the log files (octal, defaults to 0640).

    Example: To allow group members to attach to sessions, use: ZMX_DIR_MODE=0770 ZMX_LOG_MODE=0660

  9. Manage session labels with zmx set and get

    main

    Labels allow you to attach metadata to live sessions for easier discovery and filtering. Labels are in-memory and exist for the lifetime of the session.

    • Set labels: zmx set <name> k=v ... (use k= to remove a label).
    • Get labels: zmx get <name> [key].
    • Clear labels: zmx clear <name>.
    • Special name: . resolves to the current session.

    Examples:

    # Set multiple labels
    zmx set dev project=zmx env=dev
    
    # Unset a label
    zmx set dev project=
    
    # Get a specific label
    zmx get dev project
    
    # Use labels for filtering in list
    zmx list | grep project=zmx
    zmx set dev project=zmx env=dev
    zmx get dev project
  10. Run commands in the background with zmx run

    main

    Use zmx run <name> [command...] to execute commands inside a PTY. Commands are passed as-is and do not require quotes. Stdin is redirected from /dev/null to prevent interactive programs from blocking. To run a command and immediately detach, use the -d flag. You can then use zmx wait <name> to track its completion.

    Examples:

    # Basic run
    zmx run dev ls
    
    # Run and detach
    zmx run dev -d sleep 10
    
    # Pipe data to a command
    echo "hello" | zmx run dev cat
    
    # Using a heredoc
    printf "cat << 'EOF'\r\nHello $USER\r\nToday is $(date).\r\nEOF" | zmx run dev
    zmx run dev ls
    zmx run dev -d sleep 10
    echo "hello" | zmx run dev cat
  11. Manage zmx sessions with CLI commands

    main

    The zmx CLI provides several commands to manage terminal sessions. Based on the implementation, the following core commands are available:

    • zmx run <command>: Starts a new session and executes the provided command. If no command is provided, it reads from stdin. It appends a carriage return (\r) to ensure compatibility with shell readline prompts in raw mode.
    • zmx attach <session_name>: Attaches to an existing session. If the ZMX_SESSION environment variable is set, it attempts to switch to that session instead.
    • zmx history <session_name>: Fetches and prints the terminal history for a specific session.
    • zmx list: Lists all available sessions. Supports a --short flag for minimal output.
    • zmx kill <session_name>: Terminates a specific session. Use the --force flag if the session is unresponsive to clean up stale sockets.
    • zmx detach-all: Detaches the current session from the terminal.
    • zmx label <get|set|clear> <session_name>: Manages metadata labels for a session.
    • zmx write-file <file_path>: Writes the contents of stdin to a file within the session's environment.
  12. Send raw input to a session with zmx send

    main

    The zmx send <name> <text...> command sends raw text to a session's PTY input. This is a fire-and-forget operation and does not append a completion marker or track exit codes. It is ideal for interacting with TUI applications or sending commands to an existing shell.

    Note: Text is sent byte-for-byte. You must append \r manually if you want the shell to execute the input.

    Examples:

    # Send a command with carriage return
    printf 'ls -la\r' | zmx send dev
    
    # Send specific bytes
    zmx send dev $(printf '\x03')
    printf 'ls -la\r' | zmx send dev