Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
368f3b2
Server(feat[display_message]): Add Server.display_message wrapper
tony May 16, 2026
cd0f0a4
Window(feat[display_message]): Add Window.display_message wrapper
tony May 16, 2026
8c457ef
neo(feat[fields]): Add window_zoomed_flag typed field on Obj
tony May 16, 2026
a37b473
Pane(fix[reset]): Split into two cmd calls so clear-history runs
tony May 16, 2026
8d71c4e
Pane(feat[send_keys]): Support flag-only invocation (cmd=None)
tony May 16, 2026
15e83f6
Server(feat[list_buffers]): Add format_string= and filter= kwargs
tony May 16, 2026
9046b52
neo,server,session,window(feat[search]): Add search_*() methods with …
tony May 16, 2026
417af1e
exc,common(feat[subcommand]): Add LibTmuxException.subcommand and rai…
tony May 16, 2026
cb2385e
core(refactor[exc]): Migrate stderr raises to raise_if_stderr
tony May 16, 2026
5ec8fba
Server(feat[cmd]): Warn on legacy -t-in-args usage
tony May 16, 2026
b81cec4
neo(feat[fields]): Declare pane/window/session format tokens on Obj
tony May 16, 2026
17d615e
Client(feat): Add Client dataclass and Server.clients accessor
tony May 16, 2026
a667b70
Server(feat[run_shell]): Add cwd= and show_stderr= kwargs
tony May 16, 2026
38b5841
Pane(feat[capture_pane]): Add pending= kwarg
tony May 16, 2026
06e0c51
docs(CHANGES): Add 0.57.0 release entry
tony May 16, 2026
f35948b
session(docs[cmd]): Correct 0.34 versionchanged precedence rationale
tony May 16, 2026
9595ff3
neo(fix[fields]): Drop tokens missing from tmux 3.2a format_table
tony May 16, 2026
bf1a5f1
neo,client(fix[fields]): Drop new client_* tokens that crash tmux 3.2…
tony May 16, 2026
c6d5c6f
tests(display_message): Skip control-mode dispatch tests on tmux 3.2a
tony May 16, 2026
093684a
Revert "Server(feat[cmd]): Warn on legacy -t-in-args usage"
tony May 16, 2026
948a835
Revert "session(docs[cmd]): Correct 0.34 versionchanged precedence ra…
tony May 16, 2026
c6b52e0
docs(CHANGES): Drop -t-in-args deprecation note
tony May 16, 2026
1e5c824
neo(feat[scope-aware]): Make get_output_format scope+version aware
tony May 16, 2026
826e8c7
neo,client(feat[fields]): Re-expose client_* fields via scope gating
tony May 16, 2026
3120e15
neo(feat[scope-gate]): Add _SCOPE_OVERRIDES dict for fields without s…
tony May 17, 2026
650f8a8
docs(CHANGES): Document scope+version gated typed format-token fields
tony May 16, 2026
4fafb46
display_message(fix): Raise LibTmuxException on tmux stderr
tony May 16, 2026
c17e94b
Pane(docs[capture_pane]): Correct -P/pending semantics
tony May 16, 2026
1db9f0c
Server(docs[run_shell]): Note tmux chdir fallback for cwd=
tony May 16, 2026
8d0a216
tests(display_message): Skip no-text test on tmux 3.2a control-mode c…
tony May 16, 2026
a0f94d6
neo(fix[scope-gate]): Widen SCOPES_BY_LIST_CMD to admit downward cascade
tony May 16, 2026
bac1ec2
common(perf[get_version]): Memoize via @functools.cache to eliminate …
tony May 16, 2026
cec9af7
neo(fix[scope-gate]): Re-admit cursor_x/y/flag/character as pane-scope
tony May 16, 2026
e58ccb5
neo(fix[scope-gate]): Re-admit mouse_*_flag and scroll_region_* as pa…
tony May 17, 2026
0f15680
neo(fix[scope-gate]): Reclassify universal-mislabeled tokens to true …
tony May 17, 2026
b7d58e7
neo(fix[field_version]): Gate pane_dead_signal/time to tmux 3.3
tony May 17, 2026
635be51
Server(fix[clients,search_sessions]): Propagate tmux errors
tony May 17, 2026
d1fe7c1
search,list_buffers(docs[filter]): Warn on silent zero-match
tony May 17, 2026
b6d72cf
Client(docs): Warn that session/window/pane fields are attached-view …
tony May 17, 2026
f68d1e3
neo(docs[Obj],tests[cascade]): Pin downward-cascade resolution target
tony May 17, 2026
e9f01e5
neo(refactor[_UNIVERSAL_TOKENS]): Add context scope for non-list tokens
tony May 17, 2026
e8a298f
docs(CHANGES) Correct cascade direction and trim release notes
tony May 17, 2026
683b306
docs(CHANGES) Copy improvements
tony May 17, 2026
9463564
docs(CHANGES) Copy improvements
tony May 17, 2026
617d26d
docs(CHANGES,MIGRATION[breaking]): Flag str(exc) prefix change
tony May 17, 2026
929ab16
Client(feat[attached_*]): Add typed live-attachment properties
tony May 17, 2026
7f63db8
Client(fix[attached_*]): Return None for stale detached clients
tony May 17, 2026
dd8d93e
docs(Client): Clarify live attachment lookup semantics
tony May 17, 2026
0cdb8e6
docs(topics[clients,format-tokens,filtering]): Add conceptual coverag…
tony May 17, 2026
5bba3d3
common(fix[raise_if_stderr]): Flatten proc.stderr so str(exc) matches…
tony May 17, 2026
7dc96b9
Pane(fix[reset]): Bundle send-keys -R and clear-history as one tmux IPC
tony May 17, 2026
6e89966
Client(fix[refresh]): Replace -O-stripped assert with explicit ValueE…
tony May 17, 2026
cbe047c
neo(fix[_token_scope]): Fail-closed default for unclassified tokens
tony May 17, 2026
4d9b969
Client(feat[_resolve_attached]): Share one list-clients refresh acros…
tony May 17, 2026
fcc0cbf
docs(CHANGES): Drop duplicate Subcommand-tagged exceptions deliverable
tony May 17, 2026
e67e0a7
docs(MIGRATION): Add KEEP PLACEHOLDER bracket pattern
tony May 17, 2026
ca07425
Pane(fix[reset doctest]): Remove timing-fragile capture from the example
tony May 17, 2026
ab720ab
docs(search_*): Cross-link from each method to the filtering topic doc
tony May 17, 2026
07360f3
neo(docs[fetch_objs]): Propagate malformed-filter warning to the publ…
tony May 17, 2026
0e2439d
docs(CHANGES,MIGRATION): Correct (#670) PR refs to (#672)
tony May 17, 2026
961844a
Server,Window,Pane(refactor[display_message]): Warn on tmux stderr in…
tony May 17, 2026
0389ef4
Server(fix[new_session]): Hydrate Session with live tmux version
tony May 17, 2026
8c7037f
Server(fix[sessions]): Propagate tmux errors
tony May 17, 2026
b596b3d
core(fix[refresh]): Replace -O-stripped asserts across Pane/Window/Se…
tony May 17, 2026
901106c
Server(fix[*]): Treat daemon-not-up stderr as empty result
tony May 17, 2026
6fc05a7
docs(filtering[rename]): "C-side" → "native" across docstrings and CH…
tony May 18, 2026
06abeb7
Server(fix[errors]): Clarify public wording and socket failures
tony May 18, 2026
a40f3b7
Server(refactor[errors]): Refine daemon detection and docstrings
tony May 18, 2026
61688af
docs(CHANGES): Remove final 'hydration' jargon
tony May 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 219 additions & 7 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,222 @@ $ uvx --from 'libtmux' --prerelease allow python
_Notes on the upcoming release will go here._
<!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->

libtmux 0.57.0 broadens tmux support around attached clients, tmux-native
filtering, and format-token fields. {class}`~libtmux.Client` gives callers a
typed object for attached terminals, `search_*()` methods let tmux return only
matching sessions, windows, and panes, and more tmux format tokens are exposed
as typed attributes. {exc}`~libtmux.exc.LibTmuxException` now records which
tmux subcommand failed, making command errors easier to handle downstream.

### Breaking changes

#### `LibTmuxException` string form gains a subcommand prefix (#672)

When {exc}`~libtmux.exc.LibTmuxException` is raised from a libtmux method,
``str(exc)`` now begins with the originating tmux subcommand name followed
by ``": "``. For example, an error from
{meth}`~libtmux.Session.last_window` used to render as ``"can't find
window"`` and now renders as ``"last-window: can't find window"``.

``exc.args[0]`` still carries the original tmux error text, and the new
{attr}`~libtmux.exc.LibTmuxException.subcommand` attribute exposes
the tmux subcommand name as a separate field.
{func}`~libtmux.common.raise_if_stderr` is the shared helper that
populates both.

The error-payload *type* changed: ``exc.args[0]`` is now ``str``,
joined with ``"\n"`` when tmux emitted multiple stderr lines.
Previously it was ``list[str]``.

This is a serialization-format change: code that pattern-matches on
``str(exc)`` exactly, anchors a regex with ``^`` against the old
shape, or indexes ``exc.args[0]`` element-by-element will no longer
match.

```python
# Before
try:
session.last_window()
except LibTmuxException as exc:
if str(exc) == "can't find window":
...

# After — dispatch on the typed attribute
try:
session.last_window()
except LibTmuxException as exc:
if exc.subcommand == "last-window":
...

# Or — match against the original tmux error text without the prefix
try:
session.last_window()
except LibTmuxException as exc:
if exc.args and exc.args[0] == "can't find window":
...
```

Substring matches (``"can't find" in str(exc)``) and unanchored
``re.search`` patterns continue to work unchanged.

#### `Server.sessions`, `Server.clients`, and `Server.search_sessions` raise on tmux errors (#672)

Previously, a tmux command failure under
{attr}`~libtmux.Server.sessions`, {attr}`~libtmux.Server.clients`, or
{meth}`~libtmux.Server.search_sessions` could return an empty
{class}`~libtmux._internal.query_list.QueryList` indistinguishable
from "no sessions / no clients attached" or "filter matched nothing".
Those accessors now let {exc}`~libtmux.exc.LibTmuxException` propagate
for real tmux failures.

Genuine empty results — a server with no attached clients, or a
filter that matched zero sessions — still return an empty
``QueryList``. A missing or not-yet-started tmux server is also still
treated as an empty result, preserving the historic contract that a fresh
{class}`~libtmux.Server` can be safely introspected before its daemon is
up. Other tmux errors, such as socket permission failures or unsupported
flags, now surface.

```python
# Before — silent on tmux failure
if not server.clients:
log("no clients") # also runs when tmux itself crashed

# After — distinguish the two cases
try:
clients = server.clients
except LibTmuxException as exc:
log(f"list-clients failed: {exc}")
else:
if not clients:
log("no clients")
```

### What's new

#### `Client` object and `Server.clients` accessor (#672)

New {class}`~libtmux.Client` and {attr}`~libtmux.Server.clients` support tmux's
attached-client model directly. Reads like ``client.client_readonly`` and
``client.client_session`` work on the client object without dropping down to
{meth}`~libtmux.Server.cmd`.

Note that ``client.session_id`` / ``client.window_id`` / ``client.pane_id``
reflect the client's attached view when the object was read —
{meth}`~libtmux.Client.refresh` re-reads them after the client switches focus.
``client.client_name`` is the client's stable identifier.

For typed access to the live attachment, use
{attr}`~libtmux.Client.attached_session`,
{attr}`~libtmux.Client.attached_window`, and
{attr}`~libtmux.Client.attached_pane`. Each property re-reads the
client from ``list-clients`` before resolving; if tmux no longer
reports that ``client_name``, such as after the client detaches, the
property returns ``None``. Otherwise, the returned
{class}`~libtmux.Session` / {class}`~libtmux.Window` /
{class}`~libtmux.Pane` reflects where the client is attached *now*,
not where it was when the {class}`~libtmux.Client` was constructed.
Direct {meth}`~libtmux.Client.refresh` and
{meth}`~libtmux.Client.from_client_name` calls still surface missing
client lookup errors.

{attr}`~libtmux.Client.attached_pane` follows the attached session's current
window. That can differ from the per-client active pane set by
``select-pane -P``; see {attr}`~libtmux.Client.attached_pane` for the exact
behavior.

#### `Server.display_message` and `Window.display_message` (#672)

{meth}`~libtmux.Server.display_message` and
{meth}`~libtmux.Window.display_message` join the existing
{meth}`~libtmux.Pane.display_message`. Server reads like
``#{version}`` and ``#{socket_path}`` work without a pane handle;
window reads (``#{window_zoomed_flag}``, ``#{window_active_clients_list}``)
auto-bind to the window's id.

All three wrappers surface tmux stderr via :func:`warnings.warn`
rather than dropping it silently. tmux uses stderr for both genuine
errors and informational messages on some versions, so the wrappers
warn rather than raise; callers that want to escalate can wrap the
call in :func:`warnings.catch_warnings` with
``filterwarnings("error")``. See {doc}`migration` for the escalation
pattern.

#### tmux-native filtering with `search_*()` (#672)

{meth}`~libtmux.Server.search_panes`,
{meth}`~libtmux.Server.search_windows`,
{meth}`~libtmux.Server.search_sessions`, and the Session/Window
analogues take a ``filter=`` kwarg routed to tmux's ``-f`` flag. tmux
evaluates the expression and returns only matching objects.

Caveat: tmux silently expands a malformed filter expression to empty, which
it treats as false — a typo looks identical to "no matches". Verify filter
syntax against the FORMATS section of ``tmux(1)``.

#### `Pane.send_keys(cmd=None, …)` flag-only invocation (#672)

{meth}`~libtmux.Pane.send_keys` accepts ``cmd=None`` together with
``reset=True`` or ``repeat=N`` to invoke tmux's flag-only
``send-keys -R`` / ``send-keys -N <n>`` form without any trailing
key argument.

#### `Server.list_buffers(format_string=, filter=)` (#672)

{meth}`~libtmux.Server.list_buffers` gains ``format_string`` (``-F``)
and ``filter`` (``-f``) kwargs. Callers can ask tmux to return selected
fields (e.g. ``"#{buffer_name}"``) or only buffers matching an expression —
same bad-filter caveat as the ``search_*`` methods.

#### `Server.run_shell(cwd=, show_stderr=)` (#672)

{meth}`~libtmux.Server.run_shell` gains ``cwd`` (``-c``) to set the
shell command's working directory and ``show_stderr`` (``-E``) to
merge the command's stderr into the captured output. Both kwargs are
version-gated; older tmux warns and ignores the flag instead of
erroring.

#### `Pane.capture_pane(pending=True)` (#672)

{meth}`~libtmux.Pane.capture_pane` gains a ``pending`` kwarg that
returns bytes tmux has read from the pane but not yet committed to
the terminal — useful for diagnosing programs whose output stalls
mid-sequence.

#### More format-token fields on tmux objects (#672)

libtmux now asks each ``list-*`` subcommand for the format tokens that make
sense for that object and tmux version. Tokens the running tmux does not
support stay ``None`` instead of making the listing fail.

{class}`~libtmux.Pane`, {class}`~libtmux.Window`,
{class}`~libtmux.Session`, and {class}`~libtmux.Client` expose more typed
attributes for pane state (``pane_dead``, ``pane_in_mode``, ``pane_marked``,
``pane_synchronized``, ``pane_path``, ``pane_pipe``), window state
(``window_zoomed_flag``, ``window_silence_flag``, ``window_flags``), session
state (``session_marked``), and client state (``client_session``,
``client_readonly``, ``client_termtype``). Some fields describe the active
child object tmux reports with the row: for example, ``session.pane_id`` is
the active pane in the session's current window, not a separate "session
pane." See {ref}`format-tokens` for details.

### Fixes

- {meth}`~libtmux.Pane.reset` now clears pane scrollback. In 0.56.0
the history clear silently no-op'd, leaving the scrollback intact
(#650).

### Documentation

- New API page: {doc}`api/libtmux.client`.
- New {ref}`format-tokens` topic explains why some fields describe an active
child object, such as ``session.pane_id`` reflecting the active pane in the
session's current window (#672).

## libtmux 0.56.0 (2026-05-10)

libtmux 0.56.0 is the tmux command-parity release. It adds more than
50 wrappers across {class}`~libtmux.Server`, {class}`~libtmux.Session`,
50 commands across {class}`~libtmux.Server`, {class}`~libtmux.Session`,
{class}`~libtmux.Window`, and {class}`~libtmux.Pane`, filling in many
commands that previously required raw {meth}`~libtmux.Server.cmd` calls.
It also adds attached-client test support so interactive tmux commands can be
Expand All @@ -58,7 +270,7 @@ covered in headless test suites.

#### Interactive tmux commands are now scriptable (#653)

libtmux now exposes Python wrappers for tmux commands that normally depend on
libtmux now exposes Python commands for tmux that normally depend on
an attached client: {meth}`~libtmux.Pane.display_popup`,
{meth}`~libtmux.Server.display_menu`,
{meth}`~libtmux.Server.command_prompt`,
Expand All @@ -80,7 +292,7 @@ The `detach-client` API is split by the same scopes tmux actually honors:
`tmux detach-client -a [-t <keep>]`. This keeps each method to one tmux flag
group and one subprocess call.

#### tmux buffer I/O has first-class wrappers (#653)
#### tmux buffer I/O has first-class support (#653)

Named tmux buffers can now be used from libtmux without hand-built commands.
{meth}`~libtmux.Server.set_buffer`,
Expand All @@ -94,7 +306,7 @@ inter-process handoff workflows.

#### Server commands cover key bindings, clients, shell execution, and access (#653)

{class}`~libtmux.Server` gains wrappers for key-binding inspection and mutation
{class}`~libtmux.Server` gains support for key-binding inspection and mutation
({meth}`~libtmux.Server.bind_key`,
{meth}`~libtmux.Server.unbind_key`,
{meth}`~libtmux.Server.list_keys`,
Expand Down Expand Up @@ -137,7 +349,7 @@ Navigation helpers fill in the surrounding topology:
{meth}`~libtmux.Session.next_window`, and
{meth}`~libtmux.Session.previous_window`.

#### Existing wrappers expose more tmux flags (#653)
#### Improvements (#653)

Several established methods now surface tmux flags that were previously only
available by dropping to raw commands. Highlights include
Expand Down Expand Up @@ -196,7 +408,7 @@ old hardcoded socket paths. Fixes #664.

#### tmux 3.7 is within the known-version range (#653)

{data}`~libtmux.common.TMUX_MAX_VERSION` is now `"3.7"`, enabling wrappers and
{data}`~libtmux.common.TMUX_MAX_VERSION` is now `"3.7"`, enabling support and
tests for version-gated tmux 3.7 flags such as
{meth}`~libtmux.Server.command_prompt` `bspace_exit` and
{meth}`~libtmux.Server.show_messages` `terminals` / `jobs`. Installations on
Expand Down Expand Up @@ -343,7 +555,7 @@ surface from Make to Just.

### Fixes

#### `Server.new_session()` no longer races its own hydration query (#625)
#### `Server.new_session()` no longer races its own initialization query (#625)

{meth}`~libtmux.Server.new_session` now parses all required session fields from
the `tmux new-session -P` output instead of creating the session and then
Expand Down
Loading
Loading