libtmux Reference MCP Search

Architecture

Edit this page on GitHub

You don’t need anything on this page to use libtmux — every port’s objects and methods work with no setup, and Server, session, window, pane already covers the shared hierarchy, its stable IDs, and what differs between a live-refreshing handle and an immutable snapshot. This page is for when you’re curious how each port lays out the code underneath, and — the more interesting question — where the behavior actually lives, because it isn’t in the same place in every port.

Where behavior lives: on the object, or through the serverLink to section

In Python, TypeScript, Go, Rust, Java, .NET, and C++, a Session, Window, or Pane is an object (or, in Go and Rust, a value with methods) that you call directly: pane.send_keys(...), pane.sendKeys(...), pane.SetOption(...), window.split(). The object carries enough of its own identity — an ID, a reference back to its server — to act on itself. The same three operations — type into the pane, set an option on it, kill it — called directly on the object in each of these seven:

await pane.sendKeys("echo hi");
await pane.setOption("automatic-rename", "off");
await pane.kill();

Swift is the outlier, and deliberately so: Session, Window, and Pane are plain Sendable value types holding little beyond an ID and a few positional fields (see Format-token fields for exactly how little). There is no pane.sendKeys(...) or pane.kill() — every operation is instead a method on Server that takes the value as a parameter, the same three operations as above:

The practical effect is that Server is the one thing you hold onto in a Swift program; a Session or Pane you got back from a snapshot() is inert data you hand back to the server that produced it, not a handle you call things on. Every other port’s Server is also where you start, but Session/Window/Pane stay live actors once you have one.

A generated data table under a hand-written surfaceLink to section

Every port’s typed surface sits on top of the same two tmux primitives: targets (-t, which command reaches which object) and FORMATS, tmux’s own #{...} template variables that describe an object’s state. None of the eight hand-maintains the list of format tokens or options as prose comments — each generates a scope- and version-tagged table from tmux’s own source or documentation, then hand-writes a thin, idiomatic accessor layer over it:

PortGenerated tableHand-written surface
Pythonlibtmux.constants (FORMATS, gated by scope and tmux version)dataclass fields on Obj (libtmux.neo), None when a gate excludes a token
TypeScriptpackages/libtmux/src/_generated/format_fields.ts ({ scope, since, token } per row)camelCase aliases on Pane/Session/Window (packages/libtmux/src/_generated/field_aliases.ts)
Goformat_generated.go, option_generated.go (built by internal/generate/formats)(value, bool) accessor methods — Go’s own “comma ok” idiom for a gate
Rustformats.rs’s per-token macro rows (token, wire name, scope, kind, since version, absent-handling)typed methods returning Option<T>
Java(typed field accessors generated for the query layer — see Pane_/Session_ in Filtering and queries)Optional<T> for fields introduced after a port’s tmux floor
C++, Swift— (neither generated the full ~200-token catalog; see below)a fixed, curated set of non-optional struct/class fields
.NETa snapshot dictionary read at capture timetyped properties that throw IncompleteSnapshotException for a field the capture didn’t request, rather than gating on tmux version per field

C++ and Swift are both exceptions here, for related but not identical reasons. Swift’s Session/Window/Pane are small value types rather than an in-process model of the whole server, so it picked a small, fixed set of fields (index, width, height, isActive, currentCommand, currentPath, the four edge flags) instead of exposing tmux’s full format-token surface as optional properties. C++ does the same for its own reasons — a fixed nineteen-field kFields array per type, none of them std::optional — and reaches anything outside that set through pane->expand("#{...}") rather than a struct member. A token outside either curated set is reached ad hoc, not through a property — see Format- token fields for the two mechanisms side by side. This is a real, verified design choice in both ports, not a page that hasn’t been written yet.

Module layout, by portLink to section

Each port’s own top-level organization, to orient yourself before opening its source:

Naming conventionsLink to section

Every port ports tmux’s own dash-separated command and token names (new-window, automatic-rename) into its own identifier convention: Python and Rust use snake_case (new_window, automatic_rename read through get_option("automatic-rename") — the option name stays dash-separated since it’s a string tmux itself defines; only the method name changes). TypeScript, Java, .NET, and Swift use camelCase for methods (sendKeys, setOption) while, again, leaving tmux’s own option and hook names as the dashed strings tmux expects. Go and C++ use PascalCase / snake_case methods respectively, following each language’s own conventions rather than tmux’s.

Esc

Type to search.