Getting started
Create tmux sessions, windows and panes from Erlang. Capture their object graph, filter it as immutable data, and execute source queries and owned operations explicitly.
Install¶
Use Erlang/OTP 29.1.2, Rebar3 3.28.0, Linux, tmux 3.3a–3.8, and
make with a C11 compiler. Native records are experimental in OTP 29. The core
uses OTP libraries and a compiled external helper, with no NIF or third-party
runtime packages.
This checkout is unreleased. Add its core application to your rebar.config:
{deps, [ {libtmux_erlang, {git_subdir, "https://github.com/libtmux/libtmux-erlang.git", {branch, "master"}, "apps/libtmux_erlang"}}]}.Commit rebar.lock to keep the resolved commit. The
installation guide covers application startup and
building a consumer. No Hex release is advertised.
Create, capture and query¶
From this checkout, compile with the pinned tools:
$ mise exec -- rebar3 compileRun the complete program below on a private tmux server:
$ mise exec -- escript tools/run_examples.escript readmeThe program starts an empty daemon on a unique private socket, verifies its
assertions, and retires the owned server and clients. It leaves your existing
tmux sessions alone. It uses only public core APIs and OTP runtime libraries;
the archive check also runs it outside this checkout.
Save it as readme_demo.erl when reusing it in an application.
-module(readme_demo).-moduledoc "Create a workspace, query its graph and preserve captured values.".-export([run/1]).-import_record(tmux_model, [pane]).
-spec run(binary()) -> map().run(Executable) -> Options = #{executable => Executable}, {ok, Result} = tmux_server:with_owned(Options, fun(Server) -> {ok, Session} = tmux_session:create(Server, #{name => <<"workspace">>, window_name => <<"editor">>, width => 120, height => 30, launch => #{argv => [<<"/bin/cat">>]}}), {ok, Worker} = tmux_window:create(Session, #{name => <<"workers">>, launch => #{argv => [<<"/bin/cat">>]}}), {ok, Right} = tmux_pane:split(Worker, #{direction => horizontal, launch => #{argv => [<<"/bin/cat">>]}}), {ok, _} = tmux_window:select_layout(Worker, even_horizontal), {ok, _} = tmux_pane:resize(Right, #{width => 40}), {ok, Snapshot} = tmux_server:snapshot(Server), Windows = tmux_select:windows(Snapshot), Workers = #{name => <<"workers">>, panes => #{count => 2, every => #{current_command => <<"cat">>}}}, {ok, Captured} = tmux_select:one(Workers, Windows), {ok, Children} = tmux_snapshot:related(panes, Captured, Snapshot), Ids = [Id || #pane{id = Id} <:- Children], 2 = length(Ids), Criteria = #{width => #{gte => 20}, window => #{is => #{name => <<"workers">>}}}, Query = tmux_query:where(Criteria, tmux_query:panes(Server)), {ok, Fetched} = tmux_query:fetch(Query), 2 = tmux_select:size(Fetched), {ok, _} = tmux_window:rename(Worker, <<"finished">>), <<"workers">> = tmux_model:get(name, Captured), {ok, Fresh} = tmux_query:fetch(tmux_query:where( #{name => <<"finished">>}, tmux_query:windows(Server))), {ok, Renamed} = tmux_select:one(Fresh), <<"finished">> = tmux_model:get(name, Renamed), 1 = length(tmux_snapshot:sessions(Snapshot)), 2 = length(tmux_snapshot:windows(Snapshot)), 3 = length(tmux_snapshot:panes(Snapshot)), #{sessions => 1, windows => 2, panes => 3, matched_panes => 2, captured_name => <<"workers">>, immutable => true} end), Result.The result includes this checked output. Captured rows keep their original
values after a rename; actions and fetch perform explicit I/O.
tmux_server:with_owned/2 returns success after verified daemon cleanup. If cleanup fails,
its error preserves the callback outcome and ownership token for recovery.
{ "sessions": 1, "windows": 2, "panes": 3, "matched_panes": 2, "captured_name": "workers", "immutable": true}Follow the graph¶
A window can have several links in one session and belong to more than one session. Physical windows and panes remain unique; window links retain each session/index membership.
- Linked windows and relationship queries: build two sessions, link a shared window twice, and verify physical and membership counts.
- Daemon ownership: choose private startup or a borrowed socket, keep their lifetimes explicit and handle incomplete cleanup.
- Build command values: prepare a rename and resize, submit them explicitly and inspect fresh state without changing captured rows.
- Control attachments: verify connection identity, inspect attachment effects and retire the client explicitly.
- Plan and apply a workspace: validate desired sessions, windows and panes, review saved operations, and handle drift or cancellation.
- Run a job and capture its result: wait for a pane’s completion event, check its exit status, capture bytes, and cancel a separate operation.
- Local filters and source queries: Boolean criteria, quantifiers, portable JSON, cardinality, coverage and execution plans.
- Fields and operators: generated from the runtime schema.
- Scoped options: local ownership markers, explicit inheritance and sparse arrays, with a source-pinned option catalogue.
- Buffers and paste: preserve binary artifacts, inspect names and control terminal delivery.
- Rearrange panes: isolate a running job in a window and join it back, preserving its process and the original snapshot.
- Apply configuration and await a signal: load settings from stdin, observe the event and verify parse-only behavior.
- Pipe terminal output: save a job’s bytes, await its consumer acknowledgement and verify the resulting log.
- Hooks and completion: register a split callback, await its acknowledgement and remove the local hook.
- Key tables: register application bindings, inspect native listings and remove the owned table.
- Native commands: source-derived parser metadata and feature minimums, separate from library API and runtime qualification.
- API browser and source docs: build searchable native reference pages with specs, types and links to the exact source revision.
Decide whether it fits¶
The current path uses bounded one-shot tmux clients. Operations support non-blocking submission, explicit waits, cancellation and ownership transfer. An await timeout abandons that wait; it does not cancel the operation. Cancellation after dispatch can leave the tmux outcome unknown.
An explicit control attachment verifies identity on its persistent connection and owns its client cleanup. The workspace application plans and applies JSON files through a standalone escript. Control command submission and output streams remain implementation work. The support and gaps page lists what is exercised, what remains open, and the required Linux/tmux matrix. There is no control-versus-streaming performance claim.
The project policy keeps core and workspace in separate OTP applications with OTP runtime dependencies. MCP is excluded from this port’s scope.
Verify and contribute¶
$ time python3 tools/check.py mid$ time python3 tools/check.py outerThe outer check owns its tmux fixtures and tests installed consumers. See contributor setup for prerequisites and test budgets, CI for individual release/architecture results, and the MIT license.
Branding assets include the logo, icons, and social cards.