Getting started
Drive tmux from PowerShell with native cmdlets. Sessions, windows and panes come back as typed objects that an ordinary pipeline can filter.
Run a command and read its exit status with
Invoke-TmuxPaneCommand, find what a server holds
with Get-TmuxSnapshot, wait for output with
Wait-TmuxPaneText, and apply a reviewed workspace file
with Invoke-TmuxWorkspace.
Releases are numbered 0.1.N, and each release increments N. The API is not
settled: any release may change or remove exported identifiers, and the
changelog marks each break. Pin an exact version to avoid one.
Both modules are available on PowerShell Gallery: LibTmux and LibTmux.Workspace.
It needs PowerShell 7.4 or later and tmux 3.3a or newer on the same Linux host, including WSL. Compatibility lists what CI runs.
tmux release candidates are not supported: a feature can require one only
until its release ships, and a candidate such as X.Y-rcN ranks below X.Y.
See Release candidates.
Contents — Quick start · Find things · Service readiness · Choose how to run · Workspaces · Packages · From source · Leaves out · Support · Documentation
Reference — Cmdlet reference · Compatibility · Troubleshooting · MCP · License
Quick start¶
The command-completion example below needs tmux 3.4 or newer. Ordinary reads and writes support tmux 3.3a; see optional features.
Install the module from the PowerShell Gallery in PowerShell 7.4 or later
(pwsh) on Linux, including WSL. Install-PSResource ships with PowerShell
7.4; Windows PowerShell 5.1 has no such command and cannot load the module:
$ Install-PSResource -Name LibTmuxImport it:
$ Import-Module -Name LibTmux -ErrorAction StopNew-TmuxServer makes a handle on a socket and an empty tmux
configuration of its own. It contacts nothing, so your default server stays
untouched:
$server = LibTmux\New-TmuxServer ` -SocketName ('libtmux-readme-' + [Guid]::NewGuid().ToString('N')) ` -ConfigurationFile /dev/nullWithout a socket, a handle passes tmux none, so tmux picks the server: the one
in TMUX inside a pane, otherwise its default. LIBTMUX_SOCKET_PATH or
LIBTMUX_SOCKET chooses one instead; read more.
Invoke-TmuxPaneCommand runs a command in a pane and
returns a LibTmux.PaneRunResult.
New-TmuxSession starts the shell, and the finally block
removes only that session with Remove-TmuxSession:
& { $ErrorActionPreference = 'Stop' $name = 'pane-run-' + [Guid]::NewGuid().ToString('N') $session = $server | New-TmuxSession -Name $name -Command 'exec /bin/sh' try { $pane = $session | Get-TmuxPane $pane | Invoke-TmuxPaneCommand -Command 'exit 7' -Timeout 5 -Confirm:$false } finally { $session | Remove-TmuxSession -Confirm:$false }}The result has ExitStatus = 7 and TimedOut = False. A nonzero exit status
comes back in ExitStatus and raises no error. If the timeout passes first, the
command may still be running and the result has no exit status.
Command completion covers concurrency and output behavior.
Every PowerShell block on this page is a copy of a tested source. The
guide test
fails when a copy drifts, and the
installed workflow test
runs the blocks in order on an owned tmux server. Later sections reuse
$server.
Find sessions, windows, and panes¶
Get-TmuxSnapshot: what tmux holds, in one read¶
One call returns every session, window and pane as native objects.
This example builds a tree with New-TmuxSession,
Split-TmuxPane and New-TmuxWindow,
reads it, and removes it with Remove-TmuxSession. cat
keeps the panes open:
$captured = & { $ErrorActionPreference = 'Stop' $session = $server | New-TmuxSession -Name demo -WindowName editor -Command 'exec /bin/cat' try { $pane = $session | Get-TmuxPane $null = $pane | Split-TmuxPane -Horizontal -Command 'exec /bin/cat' $null = $session | New-TmuxWindow -Name logs -Command 'exec /bin/cat' ($server | Get-TmuxSnapshot).Sessions | Where-Object Name -CEQ demo } finally { $session | Remove-TmuxSession -Confirm:$false }}$captured is a LibTmux.Session that stays readable after
cleanup. Its Windows hold LibTmux.Window
objects, and each window’s Panes hold
LibTmux.Pane objects:
| Expression | Captured result |
|---|---|
$captured¶ | A Session named demo |
$captured.Windows¶ | Two Window objects: editor and logs |
$captured.Windows[0].Panes¶ | Two Pane objects in editor; logs has one |
A captured object keeps what tmux reported when you read it. Call
Get-TmuxSnapshot again to see a later change.
Snapshots and linked windows cover IDs, active children and
captured versus unavailable fields.
Filter the captured graph¶
Walk it with ordinary PowerShell. Where-Object, navigation, formatting and
property access use the captured data and start no tmux client:
$captured.Windows | Where-Object { $_.Panes.Count -gt 1 } | Select-Object Name, @{ Name = 'PaneCount'; Expression = { $_.Panes.Count } }It prints editor with a pane count of 2. logs has one pane and drops out:
Name PaneCount---- ---------editor 2Select-TmuxWindow: pick windows by criteria¶
The structured equivalent selects the one window with at least two captured
panes. -ExactlyOne reports zero or multiple matches. This query performs no
I/O either:
$captured.Windows | Select-TmuxWindow -Criteria @{ 'Panes.Count' = @{ Ge = 2 } } -ExactlyOneIt returns the editor window, the same object as $captured.Windows[0].
The query guide shows native predicates, Boolean and
relationship criteria, and explicit fresh source queries.
Wait for service readiness¶
Wait-TmuxPaneText: wait for a readiness line¶
It watches a pane’s rendered text and returns a
LibTmux.PaneWaitResult. On tmux 3.7 or newer it uses a
temporary control client; on earlier supported releases it polls instead.
Send-TmuxText and Send-TmuxKey type into
the pane first, and Get-TmuxPaneContent reads its
screen. When the application can signal completion itself, use
Wait-TmuxChannel with
a cooperative channel.
With Python 3 on PATH, this example starts an HTTP server on a free loopback
port, waits for its readiness line, then requests a page. The session’s cleanup
also stops the server:
& { $ErrorActionPreference = 'Stop' $session = $server | New-TmuxSession ` -Name ('http-' + [Guid]::NewGuid().ToString('N')) ` -Command 'exec python3 -u -m http.server 0 --bind 127.0.0.1' try { $pane = $session | Get-TmuxPane $ready = $pane | Wait-TmuxPaneText ` -Pattern '^Serving HTTP on 127\.0\.0\.1 port [0-9]+' ` -CaseSensitive -Timeout 10 -TailLines 4 -Confirm:$false if ($ready.Outcome -notin 'PresentAtEntry', 'Matched') { throw "HTTP readiness ended with $($ready.Outcome)." } $tail = $ready.Tail -join "`n" $port = [regex]::Match($tail, 'port ([0-9]+)').Groups[1].Value $uri = "http://127.0.0.1:$port/" $response = Invoke-WebRequest -Uri $uri -TimeoutSec 5 -NoProxy $ready $response.StatusCode } finally { $session | Remove-TmuxSession -Confirm:$false }}The outcome is PresentAtEntry or Matched, followed by HTTP status 200. The
wait closes any control client it creates, and the example removes only its
own session.
A text match shows the readiness line; the HTTP request checks the service. Pane-text waits cover stop patterns, bounded tails, cancellation and disclosed event loss.
Choose how to run¶
Pick a mode by who owns the client and how a failure is reported:
| Task | Use | Example |
|---|---|---|
| Read or change tmux state¶ | Typed cmdlets and pipelines | Create sessions and panes |
| Run a shell command and check its exit status¶ | Invoke-TmuxPaneCommand | Run to completion |
| Wait for a running application’s readiness line¶ | Wait-TmuxPaneText | Readiness and an HTTP health check |
| Enter a session interactively¶ | Enter-TmuxSession | Attach your foreground terminal |
| Run an ordered batch¶ | New-TmuxCommand → Invoke-TmuxChain | Compose commands |
| Reuse a connected client¶ | Connect-TmuxControl → Invoke-TmuxControlCommand | Control commands and cleanup |
| React to output and notifications¶ | Watch-TmuxEvent | Bounded event streams |
| Keep the prompt available¶ | Start-ThreadJob | Bounded background jobs |
| Run commands concurrently¶ | ForEach-Object -Parallel | Independent clients |
An older daemon refuses these before anything changes:
Invoke-TmuxPaneCommand needs tmux 3.4, and
Connect-TmuxControl and
Watch-TmuxEvent with a server need 3.7.
Other commands are refused on the releases they harm. The
optional features table lists each
one, and capabilities.json holds the same list for an
assistant or a script to read.
The raw-command cmdlets Invoke-TmuxCommand,
Invoke-TmuxChain and
Invoke-TmuxControlCommand check the command
they are given and nothing nested in it, such as an if-shell string, a
bind-key command or a brace block.
On exactly tmux 3.7, name every break-pane, including the one prefix !
runs, or upgrade to 3.7a: a nested nameless one ends the server.
What the checks do not read
lists the other failures.
Connect-TmuxControl: one client for repeated commands¶
It connects one control client. New-TmuxCommand builds a
command, Invoke-TmuxControlCommand sends it
through the client, and Disconnect-TmuxControl ends
it. Invoke-TmuxChain sends an ordered batch in one
invocation.
This example uses the private endpoint from above, reads a session name, then disconnects and removes the session it created:
& { $ErrorActionPreference = 'Stop' $session = $server | New-TmuxSession -Name control-demo -Command 'exec /bin/cat' try { $client = $server | Connect-TmuxControl -Target 'control-demo' -ErrorAction Stop try { $command = New-TmuxCommand -Name display-message -Arguments @( '-p', '#{session_name}' ) $client | Invoke-TmuxControlCommand -Command $command -ErrorAction Stop } finally { $client | Disconnect-TmuxControl -Confirm:$false } } finally { $session | Remove-TmuxSession -Confirm:$false }}The reply is control-demo. The client stays connected across commands inside
the inner try block. The control guide
covers multiple commands and cancellation.
The other modes in short:
Enter-TmuxSessionattaches your foreground terminal to a session.- Foreground cmdlets and
ForEach-Object -Paralleloccupy their calling pipeline. - Thread jobs return the prompt while they run.
Watch-TmuxEventaccepts event-count and text-byte limits.
The mode guide explains when chains merge failure attribution.
Build a workspace from a file¶
Describe a session once in YAML or JSON, review the plan, then apply it. Installing the workspace module also installs its exact core dependency:
$ Install-PSResource -Name LibTmux.WorkspaceImport both modules:
$ Import-Module -Name LibTmux, LibTmux.Workspace -ErrorAction StopImport-TmuxWorkspace: read a declaration¶
It parses YAML or JSON without contacting tmux or running the commands in it.
Get-TmuxWorkspace finds declaration files,
Resolve-TmuxWorkspace expands directories and
Test-TmuxWorkspace validates one. This declaration has
two panes in one window:
$workspace = LibTmux.Workspace\Import-TmuxWorkspace -Yaml @'session_name: readme-workspace-previewwindows: - window_name: editor panes: - shell_command: exec /bin/sh - shell_command: exec /bin/sh'@It returns a LibTmux.Workspace.WorkspaceFile and prints
nothing.
Get-TmuxWorkspacePlan: review before anything runs¶
It plans on the same private endpoint. Planning may read tmux state and creates nothing:
$workspacePlan = $workspace | LibTmux.Workspace\Get-TmuxWorkspacePlan ` -Server $server -ServerStartup CreateOrJoin -ExistingSession ErrorReview the actions. Their default view keeps command text, option values, paths
and environment values hidden; inspect an action’s
Request only in a trusted terminal:
$workspacePlan.ActionsIt lists the actions in order, from
CreateSession to
CaptureResult, with command text shown
as [redacted].
Invoke-TmuxWorkspace: preview, then apply¶
-WhatIf previews the plan without applying it:
$workspacePlan | LibTmux.Workspace\Invoke-TmuxWorkspace -WhatIfIt prints a What if: line followed by the plan’s actions. Apply that exact
plan, then remove only the session it created. The captured result stays
readable after cleanup:
$workspaceResult = & { $result = $workspacePlan | LibTmux.Workspace\Invoke-TmuxWorkspace -Confirm:$false -ErrorAction Stop try { $result } finally { $result.Session | Remove-TmuxSession -Confirm:$false }}The result is a LibTmux.Workspace.WorkspaceResult. Read its
windows:
$workspaceResult.Windows | Select-Object Name, @{ Name = 'PaneCount'; Expression = { $_.Panes.Count } }This returns editor with two panes. The
workspace guide covers failure
recovery, attaching and exporting a declaration;
ConvertTo-TmuxWorkspace turns a captured session
into a starting declaration.
Packages¶
| Product | Use it for |
|---|---|
| LibTmux¶ | Typed cmdlets, snapshots, input, capture, control clients and event streams |
| LibTmux.Workspace | Discover YAML/JSON declarations, resolve directories, review plans and create workspaces |
| LibTmux.Mcp¶ | Give an assistant tmux tools through the separately installed .NET MCP server |
LibTmux: typed cmdlets for tmux¶
Read, create, arrange and remove sessions, windows and panes, send input and capture output as native objects. The walkthroughs above run on the installed module:
- Read an object graph with
Get-TmuxSnapshotandSelect-TmuxWindow. - Check a command’s exit status with
Invoke-TmuxPaneCommand. - Wait for a readiness line with
Wait-TmuxPaneText. - Choose an execution mode, including
Watch-TmuxEvent.
Reference: every LibTmux cmdlet and the PowerShell Gallery page.
LibTmux.Workspace: workspaces from YAML or JSON¶
Describe a session once, review the plan, then apply it. The commands below run in this order:
Get-TmuxWorkspacefinds declaration files.Import-TmuxWorkspaceparses YAML or JSON without contacting tmux.Resolve-TmuxWorkspaceexpands directories andTest-TmuxWorkspacevalidates the declaration.Get-TmuxWorkspacePlanplans against a server andInvoke-TmuxWorkspaceapplies the reviewed plan;-WhatIfpreviews it.ConvertTo-TmuxWorkspaceturns a captured session into a starting declaration.
Install it with the core module, at the same version. Reference: every LibTmux.Workspace cmdlet and the PowerShell Gallery page.
LibTmux.Mcp: tmux tools for assistants¶
A separate .NET tool that lets an MCP client list, read and drive tmux panes. It needs no PowerShell. Reference: the MCP guide, the tool reference and NuGet.
Install the tool on the Unix host that runs tmux:
$ dotnet tool install --global LibTmux.Mcp \ --version 0.1.1 \ --framework net8.0Register it with Claude Code. This names the tmux socket work and offers only
the tools that read tmux:
$ claude mcp add tmux \ -e LIBTMUX_SOCKET=work \ -e LIBTMUX_TOOLSETS=inspect \ -- libtmux-mcpA client that reads a JSON configuration takes the same command and settings:
{ "mcpServers": { "tmux": { "command": "libtmux-mcp", "env": { "LIBTMUX_SOCKET": "work", "LIBTMUX_TOOLSETS": "inspect" } } }}- Find a pane with
list_sessions,list_windowsandlist_panes. - Read it with
capture_pane, orsnapshot_panefor structured state. - Wait for output with
wait_for_textand a finite timeout. - Read the selected tools and socket from the
tmux://capabilitiesresource before calling any tool.
The MCP guide covers client configuration, discovery before calling, finite waits and the other toolsets. Its test starts the installed tool with these settings and checks what it reports.
Install from source¶
Install the pinned PowerShell and .NET SDK from .tool-versions with mise:
$ mise installInstall tmux with your operating system’s package manager and check that it
is on PATH:
$ tmux -VFrom the checkout root, restore the published .NET 0.1.1 packages and build both PowerShell modules:
$ pwsh -NoLogo -NoProfile -File eng/Build.ps1 \ -RestoreRestore checks the committed lockfiles and stages both modules under
build/Modules. Developing shared .NET changes uses the optional
review-package workflow.
Start PowerShell with the staged module path:
$ LIBTMUX_REVIEW_MODULE_ROOT="$PWD/build/Modules" \ pwsh \ -NoLogo \ -NoProfileImport the exact staged manifests so an older installed module cannot shadow the new build:
$modules = $env:LIBTMUX_REVIEW_MODULE_ROOTImport-Module -Name @( "$modules/LibTmux/0.1.0/LibTmux.psd1", "$modules/LibTmux.Workspace/0.1.0/LibTmux.Workspace.psd1") -ErrorAction Stopexamples/QuickStart.ps1: a private server and three panes¶
Run the quick start from the checkout root. It
creates a private server, splits the editor window, adds logs, and returns
a captured LibTmux.Session. Show each window’s pane IDs:
(./examples/QuickStart.ps1).Windows | Select-Object Name, @{ Name = 'PaneIds' Expression = { $_.Panes.Id -join ', ' }}It prints the pane IDs from the private server:
Name PaneIds---- -------editor %0, %1logs %2Assign ./examples/QuickStart.ps1 to a variable to keep the session object. Its
Windows and Panes remain readable after
the script removes its session. The script uses a unique socket, so your
default tmux server stays untouched.
What it leaves out¶
- Live objects. A captured object keeps what tmux reported when you read it.
Get-TmuxSnapshotreads again, and snapshots and linked windows list what a capture cannot know. - An implicit server. A handle from
New-TmuxServerstarts no daemon, and importing the module connects to none. The module leavesTMUXandTMUX_PANEin your session as they are (compatibility). - Shell quoting.
Send-TmuxTextsends its text literally and does not quote it for the shell that receives it (send input). - Native Windows. Run PowerShell and tmux inside WSL, in one Linux environment. Native Windows and psmux are outside this port (compatibility).
- The MCP server. LibTmux.Mcp is a separate .NET tool that needs no PowerShell, and the modules install none of its tools (MCP guide).
Support and status¶
- Releases. Releases are numbered
0.1.N, and each release incrementsN. The API is not settled: any release may change or remove exported identifiers, and the changelog marks each break. Pin an exact version to avoid one. - PowerShell. 7.4 or later. CI runs 7.4.20 and 7.6.6.
- tmux. 3.3a or newer. CI runs 3.3a and 3.8 on every change, and 3.3a through 3.8 and tmux master before a release; other releases are unverified. Compatibility holds the contract.
- Systems. Linux, including WSL: run PowerShell and tmux in the same Linux environment. CI runs Linux x64 on every change and macOS as an advisory trial whose failures never fail the check. CI has no Windows job.
- Built on libtmux for .NET.
It lives in the same libtmux organization with
the same main author. The cmdlets return its native objects and add
PowerShell parameter binding, help, formatting and
-WhatIf/-Confirm.
Documentation¶
- Read: snapshots and filtering, structured queries, pane contents and scrollback, attached clients.
- Automate: create, send text and keys, arrange and resize, link and move windows, remove.
- Configure: options, hooks, environment for new panes.
- Coordinate: chains and control clients, event streams, jobs and parallel workers.
- Assistants: install and configure the MCP server.
- Workspaces: load, review and apply, export a starting declaration, edit a declaration.
Browse cmdlets with Get-Command -Module LibTmux and examples with
Get-Help LibTmux\New-TmuxSession -Examples. Further reading:
- Compatibility — what CI runs and the support contract
- Troubleshooting — endpoint, package, input and stream failures
- Benchmarks — installed-package workloads, correctness checks, raw samples and reproduction commands
- Contributing — the runners and development checks
- Executable guides — run against real tmux through the installed modules, and the README blocks run together in the installed workflow test
License¶
MIT. See third-party notices for bundled dependencies.