Options and hooks
tmux itself keeps two kinds of settings you can reach through any port:
options — values like automatic-rename or the status-line format — and
hooks — commands tmux runs when something happens, such as
session-renamed or after-split-window. Every port gives you one
consistent way to read, set, and remove both, at whichever scope you’re
holding — server, session, window, or pane — and most scripts never need
either: reach for this only when you want to tweak how a session behaves or
react to something inside it.
Reading and writing optionsLink to section
The four operations — read what’s set, read one value, write a value, remove it — show up in every port, but two shapes for “read” split them: some ports separate “what’s set at this exact scope” from “what’s actually in effect once inheritance is resolved,” and some don’t expose that distinction at all.
Every port’s writes return the object (or complete) synchronously with the
change already live — none of them need a refresh() before the new value
reads back. Set, read, and unset one pane option, in each port:
try await server.setOption("automatic-rename", to: "off", scope: .pane(pane))try await server.options(.pane(pane))try await server.unsetOption("automatic-rename", scope: .pane(pane))HooksLink to section
A single event can bind more than one command — tmux stores hooks as
arrays, indexed (after-new-window[0], after-new-window[1]), and every
port’s “set” either replaces the whole array or appends to it: Python and
TypeScript take the index in the hook name itself
('after-split-window[1]'); Go’s SetHooks and Swift’s at: parameter
take it separately; Java’s and TypeScript’s .append() / { append: true }
add without needing to know the next free index at all.
Set and list a session hook, in each port — session scope, deliberately, since the next section covers why window and pane scope are a trap here:
try await server.setHook("session-renamed", to: "display-message 'renamed'", in: .session(session.id.rawValue))try await server.hooks(.session(session.id.rawValue))Window and pane hook scopes are mostly fictionLink to section
This is a tmux-level fact, not a per-port choice, and it’s easy to miss
because tmux’s own set-hook -w / -p flags report success either way.
tmux keeps exactly two hook tables: global, and one per session. A
window or pane scope you pass to set-hook is accepted and then silently
discarded — the command exits 0, but the hook lands in the session’s table
regardless, and show-hooks has no per-window or per-pane listing to read
it back from at all.
Verified two independent ways:
- Java’s
Hooks.javastates it outright: “Setting one at a scope it does not belong to is accepted and then silently discarded, on every supported release — so a hook that never fires is worth checking against.all()before it is worth debugging.” - Rust actively guards against it.
Pane::set_hookandWindow::set_hookdocument returningError::OptionScopeMismatchrather than tmux’s silent success — Rust checks the name against where tmux actually keeps it and refuses the call instead of letting it silently go nowhere. - Swift’s
HookScopeenum only has two cases,.globaland.session, and C++‘sWindowandPanetypes have nohooks()orset_hook()at all —set_hookexists only onSession(andglobal_hooks()onServer). Both close off the mistake by leaving it unrepresentable in the type, rather than catching it at runtime the way Rust does.
If a hook you set on a window or a pane never seems to fire, this is the first thing to check — in every port, not just the ones above that happen to document or guard against it. Plain options, unlike hooks, do have real per-window and per-pane tables; this caveat is specific to hooks.
tmux version compatibilityLink to section
Python’s own compatibility table records the tmux floor for this API surface, verified against that port’s source:
| Feature | Minimum tmux |
|---|---|
| All options/hooks features | 3.2+ |
Window/pane hook scope flags (-w, -p) accepted | 3.2+ — see the caveat above for what “accepted” actually gets you |
client-active, window-resized hooks | 3.3+ |
pane-title-changed hook | 3.5+ |
Whether another port’s floor differs for any of these was not verified for this page — check that port’s own compatibility notes before relying on a specific version across all eight.