package tmux import ( "fmt" "os/exec" "strings" ) // CreateSession creates a new tmux session with the given name and working directory. func CreateSession(name, workDir string) error { return run("new-session", "-d", "-s", name, "-c", workDir) } // SetOption sets a tmux user option on a session. func SetOption(session, key, value string) error { return run("set-option", "-t", session, key, value) } // GetOption reads a tmux user option from a session. func GetOption(session, key string) (string, error) { out, err := output("show-option", "-t", session, "-v", key) if err != nil { return "", err } return strings.TrimSpace(out), nil } // SendKeys sends keystrokes to a tmux session. func SendKeys(session, keys string) error { return run("send-keys", "-t", session, "-l", keys) } // SendEnter sends an Enter keystroke to a tmux session. func SendEnter(session string) error { return run("send-keys", "-t", session, "Enter") } // ListSessions returns all tmux session names. func ListSessions() ([]string, error) { out, err := output("list-sessions", "-F", "#{session_name}") if err != nil { // No sessions is not an error if strings.Contains(err.Error(), "no server running") || strings.Contains(err.Error(), "no sessions") { return nil, nil } return nil, err } lines := strings.Split(strings.TrimSpace(out), "\n") var sessions []string for _, line := range lines { line = strings.TrimSpace(line) if line != "" { sessions = append(sessions, line) } } return sessions, nil } // ListSessionsWithPrefix returns tmux sessions matching a prefix. func ListSessionsWithPrefix(prefix string) ([]string, error) { all, err := ListSessions() if err != nil { return nil, err } var result []string for _, s := range all { if strings.HasPrefix(s, prefix) { result = append(result, s) } } return result, nil } // KillSession kills a tmux session. func KillSession(session string) error { return run("kill-session", "-t", session) } // HasSession checks if a tmux session exists. func HasSession(session string) bool { err := run("has-session", "-t", session) return err == nil } // SessionAttachedClients returns the number of clients attached to a session. func SessionAttachedClients(session string) (int, error) { out, err := output("display-message", "-t", session, "-p", "#{session_attached}") if err != nil { return 0, err } val := strings.TrimSpace(out) if val == "" { return 0, nil } n := 0 for _, c := range val { if c >= '0' && c <= '9' { n = n*10 + int(c-'0') } } return n, nil } // SendCtrlC sends Ctrl-C to interrupt the current pane command. func SendCtrlC(session string) error { return run("send-keys", "-t", session, "C-c") } // RespawnPane replaces the current pane content with a new command. func RespawnPane(session, cmd string) error { return run("respawn-pane", "-k", "-t", session, cmd) } func run(args ...string) error { cmd := exec.Command("tmux", args...) out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("tmux %s: %w: %s", args[0], err, string(out)) } return nil } func output(args ...string) (string, error) { cmd := exec.Command("tmux", args...) out, err := cmd.CombinedOutput() if err != nil { return "", fmt.Errorf("tmux %s: %w: %s", args[0], err, string(out)) } return string(out), nil }