# LibTmux
LibTmux is a Perl tmux client library for creating sessions, arranging
windows, sending input, capturing output, and filtering observed graphs.
Its runtime uses the Perl standard library. It requires Perl 5.44 and
Linux x86_64 LP64, including WSL; tmux 3.3a–3.8 is supported.
[Quick start](#create-and-inspect-a-session) ·
[Filter records](#reuse-a-filter-with-ordinary-hash-records) ·
[Guide](https://libtmux.org/en/perl/latest/guides/) ·
[API reference](https://libtmux.org/en/perl/latest/reference/)
## Install from source
This development distribution is not published on CPAN. Install Perl and cpm
as described in the [installation guide](https://github.com/libtmux/libtmux-perl/blob/master/docs/installation.md),
then run this from the core distribution directory:
```console
$ cpm install \
--global \
.
```
## Create and inspect a session
Run [examples/quickstart.pl](examples/quickstart.pl) from this directory after
installation. An unused socket starts a new server; an existing server keeps
all sessions except the one the example creates and removes.
```console
$ perl examples/quickstart.pl "${TMPDIR:-/tmp}/libtmux-perl-$$.sock"
```
```perl
use v5.44;
use LibTmux::Server;
my $socket = shift @ARGV // die "Usage: $0 SOCKET_PATH [TMUX]\n";
my $server = LibTmux::Server->new(
socket_path => $socket,
executable => shift(@ARGV) // 'tmux',
config_file => '/dev/null',
);
my ($session, $snapshot, $failure);
try {
$session = $server->new_session(
name => 'perl-quickstart',
command => [$^X, '-e', 'while () { print }'],
);
my $editor = $session->window_links->one->window;
$server->rename_window(window => $editor, name => 'editor');
$server->split_pane(
pane => $editor->panes->one,
horizontal => true,
command => [$^X, '-e', 'while () { print }'],
);
$server->new_window(
session => $session,
name => 'logs',
command => [$^X, '-e', 'while () { print }'],
);
$snapshot = $server->snapshot;
}
catch ($error) {
$failure = $error;
$session //= $error->created if $error isa LibTmux::X::Creation;
}
if ($session) {
try { $server->kill_session(session => $session) }
catch ($cleanup) {
warn "session cleanup failed: $cleanup\n";
$failure //= $cleanup;
}
}
die $failure if defined $failure;
# The captured graph remains readable after its live session has gone.
for my $link ($snapshot->session_by_id($session->id)->window_links->@*) {
my $count = $link->window->panes->count;
say $link->window->name, ': ', $count, $count == 1 ? ' pane' : ' panes';
}
```
Expected output:
```text
editor: 2 panes
logs: 1 pane
```
[`Server`](https://libtmux.org/en/perl/latest/reference/libtmux-server/) owns
explicit commands. Captured
[`Snapshot`](https://libtmux.org/en/perl/latest/reference/libtmux-snapshot/)
values and their relationships remain readable after cleanup. The example
imports only installed modules and needs no author fixture or Python.
The [longer workflow](examples/workflow.pl) adds layout selection, input,
capture, environment, compiled filters, and native query verification.
## Filter panes with ordinary Perl
The longer workflow filters its captured editor panes with a
[`Selection`](https://libtmux.org/en/perl/latest/reference/libtmux-selection/):
```perl
my $snapshot = $server->snapshot;
my $panes =
$snapshot->windows->one({name => 'editor'})
->panes->where({width => {-gte => 1}})
->grep(sub ($pane) { $pane->height > 0 });
my @ids = map { $_->id } $panes->@*;
my $active = $panes->one({active => true});
say 'editor panes: ', scalar @ids;
say 'active pane belongs to ', $active->window->name;
```
`where` and `grep` return new selections in every calling context and preserve
order and duplicates. `grep` passes exactly one entity in scalar context and
localizes `$_`. `one` rejects absence and ambiguity; `one_or_undef` permits
absence. Use `exists` or `count` to test membership. An empty selection is
still a true reference.
## Reuse a filter with ordinary hash records
[`Filter`](https://libtmux.org/en/perl/latest/reference/libtmux-filter/) also
works without a tmux server. Run [examples/filters.pl](examples/filters.pl):
```console
$ perl examples/filters.pl
```
```perl
my $filter = LibTmux::Filter->new(
entity => 'pane',
where => {
active => true,
current_command => {-in => [qw(perl vim)]},
width => {-gte => 80},
},
);
my @records = (
{id => '%1', active => true, current_command => 'perl', width => 120},
{id => '%2', active => false, current_command => 'perl', width => 120},
{id => '%3', active => true, current_command => 'vim', width => 40},
);
my @matches = grep { $filter->matches($_) } @records;
say 'matching records: ', join ', ', map { $_->{id} } @matches;
```
It selects `%1`. The complete program also round-trips the filter through
versioned JSON. Schemas validate all criteria before evaluation, including on
empty inputs; arbitrary keys never become arbitrary method calls.
## Navigate links and handle failures
A physical window can belong to several sessions.
[`WindowLink`](https://libtmux.org/en/perl/latest/reference/libtmux-windowlink/)
owns its session-relative index and active state. `Pane->window` always means
the captured physical window. A new snapshot observes later state; existing
handles retain their original graph.
Failures are typed [exceptions](https://libtmux.org/en/perl/latest/reference/libtmux-x/)
with causes and bounded process diagnostics. Command failures never become
empty selections. Captured targets retain server-generation protection;
raw IDs deliberately address current resources.
Read the [filtering guide](https://libtmux.org/en/perl/latest/guides/filtering/),
[graph guide](https://libtmux.org/en/perl/latest/guides/graph/),
and [error guide](https://libtmux.org/en/perl/latest/guides/errors/).
Installed POD is available with `perldoc LibTmux::Server` and
`perldoc LibTmux::Selection`.
## Add async execution or workspace loading
[LibTmux-Async](https://github.com/libtmux/libtmux-perl/tree/master/packages/async)
adds Future-returning operations.
[LibTmux-Workspace](https://github.com/libtmux/libtmux-perl/tree/master/packages/workspace)
loads JSON through these public core APIs. Core loads neither distribution.
Persistent control and streaming APIs are deferred.
Each native archive owns its metadata, test requirements, README, license,
logo assets, and consumer examples. The
[repository examples runner](https://github.com/libtmux/libtmux-perl/blob/master/examples/README.md)
verifies them against owned real tmux servers and installed artifacts.