LibTmux.FSharp
0.0.0-alpha.17
Prefix Reserved
dotnet add package LibTmux.FSharp --version 0.0.0-alpha.17
NuGet\Install-Package LibTmux.FSharp -Version 0.0.0-alpha.17
<PackageReference Include="LibTmux.FSharp" Version="0.0.0-alpha.17" />
<PackageVersion Include="LibTmux.FSharp" Version="0.0.0-alpha.17" />
<PackageReference Include="LibTmux.FSharp" />
paket add LibTmux.FSharp --version 0.0.0-alpha.17
#r "nuget: LibTmux.FSharp, 0.0.0-alpha.17"
#:package LibTmux.FSharp@0.0.0-alpha.17
#addin nuget:?package=LibTmux.FSharp&version=0.0.0-alpha.17&prerelease
#tool nuget:?package=LibTmux.FSharp&version=0.0.0-alpha.17&prerelease
LibTmux.FSharp
Compose tmux from F# with task helpers, native sequences, and typed portable
filters over the existing LibTmux
objects. This package and the core share a repository, release version, and
primary author in the libtmux organization.
Run isolated tmux · Connect to running tmux · Filter snapshots · Read control events
A portable relation filter is an F# value after installing the package below:
open LibTmux.FSharp
let sessionsWithCommands commands =
Filter.oneOf commands PaneFields.currentCommand
|> Filter.any WindowFields.panes
|> Filter.any SessionFields.windows
let editorFilter = sessionsWithCommands [ "nvim"; "vim" ]
printfn "capture depth: %A" (Filter.toDocument editorFilter).RequiredSnapshotDepth
This prints capture depth: Panes. The filter describes sessions containing
an editor pane; constructing it makes no tmux call. The complete example below
creates a server, captures its object graph, and applies a filter.
Alpha API: pin a package version and upgrade deliberately. The walkthrough
uses .NET SDK 10 and tmux 3.2a through 3.7c on Linux or macOS. The package
targets net8.0 and net10.0.
Quick start
Install tmux and make sure it is available on PATH.
Create an F# console project:
$ dotnet new console --language F# --framework net8.0 --output tmux-demo
Enter it:
$ cd tmux-demo
Add the F# package; it brings in the matching LibTmux core package and
records the selected prerelease version in the project:
$ dotnet package add LibTmux.FSharp --prerelease
Replace Program.fs with this complete program:
open System
open System.Threading
open LibTmux
open LibTmux.FSharp
let runAsync () =
task {
let binary =
match Environment.GetEnvironmentVariable("LIBTMUX_TMUX") with
| null
| "" -> "tmux"
| value -> value
let options =
ServerConnectionOptions(
SocketName = "fsharp-" + Guid.NewGuid().ToString("N"),
ConfigurationFile = "/dev/null",
TmuxBinaryPath = binary
)
use! ownedServer = LibTmux.Server.CreateOwnedAsync(options, CancellationToken.None)
use! _ownedSession =
ownedServer.Value.CreateOwnedSessionAsync(
NewSessionRequest(Name = "demo", WindowName = "shell", Command = "/bin/sh"),
CancellationToken.None
)
use! _ownedWorker =
ownedServer.Value.CreateOwnedSessionAsync(
NewSessionRequest(Name = "worker", WindowName = "idle", Command = "/bin/sh"),
CancellationToken.None
)
let! connected = LibTmux.Server.ConnectAsync(options, CancellationToken.None)
let! captured =
connected |> Server.capture CancellationToken.None SnapshotDepth.Panes
let session =
captured.Sessions |> Seq.find (fun candidate -> candidate.Name = "demo")
let window = session.Windows |> Seq.exactlyOne
let pane = window.Panes |> Seq.exactlyOne
let command =
pane
|> Pane.currentCommand
|> Option.defaultWith (fun () -> failwith "The captured pane has no command.")
let localMatches =
captured.Panes
|> Seq.filter (fun candidate -> candidate.Id = pane.Id)
|> Seq.length
let hasPane =
Filter.eq pane.Id PaneFields.id
|> Filter.any WindowFields.panes
|> Filter.any SessionFields.windows
let selected =
captured.Sessions
|> Query.matching hasPane
|> Selection.exactlyOne
|> Result.defaultWith (fun error -> failwithf "Expected one matching session: %A" error)
if
captured.Sessions.Count <> 2
|| captured.Panes.Count <> 2
|| window.Name <> "shell"
|| localMatches <> 1
|| selected.Id <> session.Id
then
failwith "The captured graph and portable filter did not agree."
printfn "%s / %s / %s" session.Name window.Name command
printfn "local pane matches: %d" localMatches
printfn "portable match: %s" selected.Name
}
runAsync().GetAwaiter().GetResult()
Run it:
$ dotnet run
It prints demo / shell / sh, local pane matches: 1, and
portable match: demo on the Linux tmux used for validation. The captured
command name can differ by shell. The program checks the graph and query
result before printing. A second worker session also runs a shell; filtering
by the captured pane ID selects one session from two.
CreateOwnedAsync starts a server on a unique socket. ConnectAsync attaches
a second handle to that socket. use! closes the owned sessions and server
when the task ends. Server.capture performs one explicit snapshot
acquisition. Traversal through
captured.Sessions → session.Windows → window.Panes, Seq.filter, and
Query.matching then use captured data locally. The portable filter matches
a session through its windows and panes. It does not send a native tmux filter.
The quickstart source
is the published block. CI restores only LibTmux.FSharp as a direct package
reference from freshly packed artifacts, then runs this program against real
tmux on both target frameworks.
Existing tmux
The quickstart's ConnectAsync(options, ct) attaches to a running server by
socket name. In an application, use your server's socket name and omit the
owned setup. ConnectAsync never starts tmux. A bare ConnectAsync() resolves
the default socket; socket selection
explains the configuration order. Code running inside a tmux pane can use
Server.FromEnvironment() to locate that pane's server.
Choose a read
| Need | F# call | Result |
|---|---|---|
| Live state | server.GetSessionsAsync(ct) or Server.capture ct depth server |
Task; contacts tmux |
| Application-specific local filter | captured.Panes |> Seq.filter predicate |
Lazy sequence over captured objects |
| Portable relation filter | Filter.any then Query.matching |
Materialized IReadOnlyList; no tmux call |
| A missing live entity | Server.tryFindPane ct id server |
Task<Pane option>; other failures still throw |
| Exactly one match | Selection.exactlyOne source |
Result distinguishing zero from many |
Captured sessions, windows, panes, and IDs are the core .NET types. A window
linked into more than one session has contextual placements; filtering keeps
their order and multiplicity. An uncaptured relationship raises
IncompleteSnapshotException rather than behaving as empty.
Keep going
- Split panes and send keys: owned scopes and live mutation.
- Filter snapshots and supported fields: quantifiers, capture depth, and the shared schema.
- Choose an execution mode and read control events: scoped clients, consumptive streams, cancellation, and command chains.
- Call the core from F# and browse signatures.
- Use tmux from an assistant:
LibTmux.Mcpis a separate .NET tool. - Read benchmark records: measured core execution modes and benchmark methods.
Compatibility
| Area | Contract and verification |
|---|---|
| .NET | Targets .NET 8 and .NET 10 and uses the matching LibTmux package version. |
| tmux | Required Linux CI runs the repository's F# integration example against tmux 3.2a, 3.3a, 3.4, 3.5, 3.6, 3.7a, 3.7b, and 3.7c on both target frameworks. The README quickstart runs against the runner's tmux in the package workflow. |
| Operating systems | Linux is required CI. An advisory macOS arm64 job runs the example with Homebrew tmux on manual dispatch. Native Windows tmux is unsupported. |
| Trimming and NativeAOT | A Linux consumer publishes and runs the static snapshot and native Seq route on both frameworks. |
Selection.exactlyOne is unsupported under NativeAOT while FSharp.Core 10.1.302
emits trim and AOT diagnostics for its Result return type.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- FSharp.Core (>= 10.1.302)
- LibTmux (= 0.0.0-alpha.17)
-
net8.0
- FSharp.Core (>= 10.1.302)
- LibTmux (= 0.0.0-alpha.17)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.0.0-alpha.17 | 38 | 9/27/2026 |
Alpha. LibTmux, LibTmux.FSharp, LibTmux.Testing, LibTmux.Query.Json, LibTmux.Extensions.DependencyInjection, LibTmux.Workspace, LibTmux.Workspace.Cli and LibTmux.Mcp share one version, so this release covers them together. What changed, per package and per release: https://github.com/libtmux/libtmux-dotnet/blob/master/CHANGELOG.md The public API is not settled and may change without notice between prereleases; pin an exact version.