libtmux-go

module
v0.0.1-alpha.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 16, 2026 License: MIT

README

libtmux for Go

Go Reference tests

Alpha software. Releases carry an -alpha prerelease tag, the API is not settled, and any release may change or remove exported identifiers without a deprecation period. Pin an exact version. Not recommended for production.

Drive tmux from Go: sessions, windows, and panes as typed values, every tmux option and hook as a typed accessor, and errors classified by what tmux actually refused.

  • No runtime dependencies. The core module imports only the standard library.
  • Go 1.23+, tmux 3.2a through 3.7b, checked against every release in that range on each change.
  • Records never refresh behind you. A Session you hold is what tmux said when you asked, not a live handle that changes underneath.
$ go get github.com/libtmux/libtmux-go/tmux@v0.0.1-alpha.1

Modules are tagged per directory, so the consumers carry their own versions: mcp/v0.0.1-alpha.2, workspace/v0.0.1-alpha.1.

ContentsQuick start · Querying · Choosing a mode · Watching tmux · Packages · For agents · Testing your code · Documentation

Quick start

Make a window, split it, send a command into the new pane:

windowName := "work"
window, err := session.NewWindow(ctx, tmux.NewWindowRequest{Name: &windowName})
if err != nil {
	return fmt.Errorf("create window: %w", err)
}
pane, err := window.SplitPane(ctx, tmux.SplitPaneRequest{
	Direction: tmux.PaneDirectionRight,
})
if err != nil {
	return fmt.Errorf("split window: %w", err)
}
command := "printf 'libtmux ready\\n'"
if err := pane.SendKeys(ctx, tmux.SendKeysRequest{Command: &command, Literal: true}); err != nil {
	return fmt.Errorf("send command: %w", err)
}

Every Go block below marked this way is generated from a program in examples/ that is compiled, linted, run against a real tmux, and swept across every supported release — so none of it can drift from code that works.

Runnable: examples/quickstartgo -C examples run ./quickstart.

What querying looks like

Two ways to ask, and they answer the same question at different costs.

Let tmux filter, which sends one command and gets back only matches:

live := tmux.TmuxFilter("#{==:#{session_name},libtmux-filter}")
sessions, err := server.SearchSessions(ctx, &live)

Or read once and filter in Go, when you want several answers from one read:

snapshot, err := server.Snapshot(ctx)
if err != nil {
	return err
}
predicate, err := tmux.PaneActiveIs(true).Predicate()
if err != nil {
	return err
}
active := tmuxq.Where(snapshot.Panes(), predicate)

Typed filters compose, and the generated ones push down into tmux's own -f where tmux can evaluate them:

filter := tmux.PaneFilter{
	Active:      tmux.Ptr(true),
	CurrentPath: tmux.Ptr("/home/you/project"),
}
panes, err := server.SearchPanes(ctx, &filter)

Runnable: examples/filter-query.

Choosing a mode

Every command starts a tmux process unless you turn something on. Each switch is one line to turn on, one to take back, and independent of the others:

Mode Turn it on Cost Reach for it
process nothing, the default a tmux process each one-shot commands
control OpenControlPool one tmux client more than a few commands
concurrent Connections: N N tmux clients parallel readers
chained NewPlan then Run no records back builds and layouts
streaming Notifications a connection watching what a pane does

Each row changes how a command reaches tmux and none changes what it means, which is the property the benchmark table gates on. One switch is deliberately not a row, because it does change meaning: ServerOptions.Unsupported decides whether a request naming a flag the running tmux does not have is refused — the default — or carried out without it and reported to a warning handler.

A control connection carries commands without starting a process for each. It is a tmux client while open — it appears in list-clients and counts toward session_attached — which is why it is chosen rather than automatic:

_, connected, pool, err := server.OpenControlPool(ctx, session, tmux.ControlPoolRequest{})
if err != nil {
	return fmt.Errorf("open control pool: %w", err)
}
defer func() { _ = pool.Close() }()

The pool returns the session bound to the connection. The one passed in still starts a process per command, so the returned value is the one to keep.

A plan records commands instead of running them, sends the ones needing no answer together, and hands back a reference to what a step will create — so a build is written in one pass:

plan := tmux.NewPlan()
plan.SelectLayout(window.Ref(), tmux.SelectLayoutRequest{Layout: "tiled"})
editor := plan.SplitPane(window.Ref(), tmux.SplitPaneRequest{Attach: true})
plan.SetPaneTitle(editor, "editor")
plan.SendKeys(editor, tmux.SendKeysRequest{Command: tmux.Ptr("echo built")})
plan.DisplayMessage(editor, "#{pane_title}")

Runnable: examples/fast-path and examples/planned-build. BENCHMARKS.md is what each mode costs, measured on every supported tmux.

Watching tmux

tmux pushes what happens down an open connection, so a change is heard once, when it happens, rather than found by a poll that has to guess how often to ask:

for notification, err := range control.Notifications(ctx) {
	if err != nil {
		return fmt.Errorf("read notification: %w", err)
	}
	fmt.Printf("notification: %s\n", notification.Kind())
	if notification.Kind() == tmux.ControlNotificationSessionRenamed {
		fmt.Println("heard the rename")
		return nil
	}
}

Runnable: examples/control-mode-subscribe.

Packages

Package Source Reference What it is
tmux tmux/ pkg.go.dev The library. Sessions, windows, panes, options, hooks, formats, filters, snapshots, plans.
tmuxtest tmux/tmuxtest/ pkg.go.dev Run your program in a real tmux and assert on what it drew.
tmuxq tmuxq/ pkg.go.dev Model-free generic helpers for slices and iter.Seq.

Three more ship as separate modules, so go get on the library pulls in none of them:

Module Source Reference What it is
mcp mcp/ pkg.go.dev A tmux server for AI agents over the Model Context Protocol. Install it as a binary.
workspace workspace/ pkg.go.dev Loads tmuxp-style YAML workspaces and builds them.
benchmarks benchmarks/ Prints what each way of reaching tmux costs.
For agents

mcp/ is a standalone Model Context Protocol server that gives an agent one tmux server: create panes, send keys, read output, wait for text.

$ go install github.com/libtmux/libtmux-go/mcp/cmd/libtmux-mcp@v0.0.1-alpha.2

See mcp/README.md for client configuration, and mcp/TOOLS.md for the tool reference.

Testing your own code

tmux/tmuxtest runs your program inside a real tmux and lets a test assert on what it drew, with no sleeps. Run it, wait for what it draws, type at it:

pane := tmuxtest.RunInPane(ctx, t, "printf 'ready\\n'; cat")

tmuxtest.WaitForText(ctx, t, pane, "ready")
tmuxtest.Type(ctx, t, pane, "a line for the program")
tmuxtest.WaitForLine(ctx, t, pane, "a line for the program")

A wait that runs out fails with the screen the pane last held, rather than sending you back to add a print statement:

tmuxtest: pane %1 never showed a line containing "ready"
the pane showed 3 line(s):
    | tmuxtest$ ./mytui --watch
    | loading widgets
    | connecting

It works for a test whose subject is tmux itself too, giving a server on its own socket that is killed when the test ends:

func TestSomething(t *testing.T) {
	ctx := context.Background()
	server := tmuxtest.NewServer(ctx, t)

	session, err := server.NewSession(ctx, tmux.NewSessionRequest{Name: "under-test"})
	// ...
}

For a machine with no tmux at all, ServerOptions.Runner replaces process execution entirely.

Documentation

The package documentation is the reference, written to be read start to finish rather than searched:

$ go doc github.com/libtmux/libtmux-go/tmux

It opens with a task index, then the rule mapping a tmux command to its Go method — kill-pane is Pane.Kill, rename-session is Session.Rename — so a command usually leads to its method without a lookup.

DESIGN.md The conventions this package holds itself to, and the bakeoffs behind them
PARITY.md How the surface is checked against the Python libtmux
BENCHMARKS.md What each way of reaching tmux costs
CHANGELOG.md What each release changed
CONTRIBUTING.md The gates a change has to pass
SECURITY.md What this software executes, and how to report a hole in it
AGENTS.md Working on this repository
examples/ Runnable programs for each of the above

License

MIT. See LICENSE.

Directories

Path Synopsis
mcp module
Package tmux provides a typed, context-aware API with tmux 3.2a as its minimum supported version, tested through tmux 3.7b.
Package tmux provides a typed, context-aware API with tmux 3.2a as its minimum supported version, tested through tmux 3.7b.
internal/generate/docs command
Command docs keeps the Go in the repository's markdown identical to Go that compiles.
Command docs keeps the Go in the repository's markdown identical to Go that compiles.
internal/generate/filters command
Command filters generates the typed tmux snapshot filters.
Command filters generates the typed tmux snapshot filters.
internal/generate/formats command
Command formats generates tmux format metadata and typed accessors.
Command formats generates tmux format metadata and typed accessors.
internal/generate/options command
Command options generates tmux option and hook metadata and typed value surfaces.
Command options generates tmux option and hook metadata and typed value surfaces.
internal/goname
Package goname converts tmux and Python lower_snake_case names to the Go exported spelling this module uses.
Package goname converts tmux and Python lower_snake_case names to the Go exported spelling this module uses.
internal/tmuxcmd
Package tmuxcmd contains the private tmux process boundary.
Package tmuxcmd contains the private tmux process boundary.
tmuxtest
Package tmuxtest runs your program inside a real tmux and lets a test assert on what it drew.
Package tmuxtest runs your program inside a real tmux and lets a test assert on what it drew.
Package tmuxq queries materialized values with caller-supplied predicates.
Package tmuxq queries materialized values with caller-supplied predicates.
workspace module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL