LibTmux.Workspace 0.1.1

Prefix Reserved
dotnet add package LibTmux.Workspace --version 0.1.1
                    
NuGet\Install-Package LibTmux.Workspace -Version 0.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="LibTmux.Workspace" Version="0.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LibTmux.Workspace" Version="0.1.1" />
                    
Directory.Packages.props
<PackageReference Include="LibTmux.Workspace" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add LibTmux.Workspace --version 0.1.1
                    
#r "nuget: LibTmux.Workspace, 0.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package LibTmux.Workspace@0.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=LibTmux.Workspace&version=0.1.1
                    
Install as a Cake Addin
#tool nuget:?package=LibTmux.Workspace&version=0.1.1
                    
Install as a Cake Tool

LibTmux.Workspace

LibTmux.Workspace libtmux.org

Build tmux sessions from tmuxp YAML or JSON workspace files, on top of LibTmux.

Parse a file, review the plan of what it will do, and apply it or build in one call. It suits launchers, devcontainer entrypoints, and internal CLIs that already describe sessions in tmuxp YAML and want typed results.

Add it to your project:

$ dotnet add package LibTmux.Workspace

Releases. 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.

Contents — Quick start · Plan and apply · Read a file · Options and commands · Startup · Failures · Library or command · Leaves out · Support

Quick start

Build a two-window session from a YAML declaration. The block uses your configured LibTmux Server and cancellation token; the LibTmux quick start covers choosing a socket:

WorkspaceFile workspace = WorkspaceFile.Parse("""
    session_name: api
    windows:
      - window_name: editor
        panes:
          - shell_command: echo editing
      - window_name: server
        panes:
          - shell_command: echo serving
    """);

WorkspaceResult result = await new WorkspaceBuilder(server).BuildAsync(workspace, ct);
if (result.Session.Name != "api" || result.Windows.Count != 2)
{
    throw new InvalidOperationException("Expected the session api with two windows.");
}

It runs against a tmux server of its own and prints nothing. The session api has windows editor and server, and the block throws otherwise.

Every csharp run block on this page runs the same way, with server, session, and ct already in hand.

Plan and apply a workspace

WorkspaceBuilder.PlanAsync: review before anything runs

A plan lists what will happen. ApplyAsync executes it, and Validate checks a declaration locally before any tmux endpoint is chosen:

WorkspaceFile workspace = WorkspaceFile.Parse("""
    session_name: api
    start_directory: /tmp
    windows:
      - window_name: editor
        panes:
          - shell_command: echo editing
      - window_name: server
        panes:
          - shell_command: echo serving
    """);

WorkspaceBuilder.Validate(workspace);
WorkspaceBuilder builder = new(server);
WorkspacePlan plan = await builder.PlanAsync(workspace, cancellationToken: ct);
foreach (WorkspaceAction action in plan.Actions)
    Console.WriteLine(action);

WorkspaceResult result = await builder.ApplyAsync(plan, ct);
Console.WriteLine($"{result.Session.Name}: {result.Windows.Count} windows");

It prints one line per action, then api: 2 windows.

  • Planning observes the endpoint and creates nothing. Displaying or enumerating the plan runs no tmux command.
  • ApplyAsync rechecks the daemon and session it observed before it executes.
  • From the third pane in a window, the plan adds an ArrangePanes action that tiles the existing panes to make room. It can resize programs already running.

WorkspaceBuilder.BuildAsync: plan and apply in one call

It uses the default policies, as the quick start does. It sends input immediately; it does not wait for shells to be ready or for commands to finish. Startup covers waiting.

Read a workspace file

WorkspaceFile.Parse: YAML or JSON into a declaration

It reads the text without contacting tmux. Unknown keys, wrong value shapes, multiple YAML documents, and inputs over 1 MiB throw WorkspaceFormatException, naming the property path, line, and column.

WorkspaceFile.Resolve: absolute directories

Resolve directories relative to the file when reading from disk:

string source = Path.GetFullPath("session.yaml");
WorkspaceFile fromDisk = WorkspaceFile.Parse(File.ReadAllText(source))
    .Resolve(Path.GetDirectoryName(source)!);
  • A window inherits the session directory, and a pane inherits its window's. Neither call contacts tmux or checks that a directory exists.
  • $NAME and ${NAME} expand from the variables argument. A leading ~ needs an absolute HOME value there, and $$ is a literal dollar sign. Unknown directory variables fail.
  • The resolver does not read process environment variables.

WorkspaceFile.FromSnapshot: a captured session as a declaration

Capture through SnapshotDepth.Panes first; a missing relation throws IncompleteSnapshotException:

Server captured = await server.CaptureSnapshotAsync(SnapshotDepth.Panes, ct);
Session source = captured.Sessions.Single(value => value.Id == session.Id);
WorkspaceFile frozen = WorkspaceFile.FromSnapshot(source);
WorkspaceFile resolved = frozen.Resolve("/tmp");
Console.WriteLine($"{resolved.SessionName}: {resolved.Windows.Count} windows");

It prints the session name and its window count.

The declaration keeps window and pane order, window indexes, names, layouts, pane directories, and the selected window and pane. It omits commands, environment, options, terminal text, entity IDs, and pane indices.

Set options, environment, and commands

Options: when each one applies

session_name: workers
global_options:
  default-shell: /bin/sh
windows:
  - window_name: workers
    layout: main-horizontal
    options:
      main-pane-height: 5
    options_after:
      synchronize-panes: on
    panes:
      - shell_command: echo left
      - shell_command: echo right
  • Session options and global_options apply before the windows are created; window options apply before startup input and further splits; options_after apply after all pane input and the final layout.
  • Global options change defaults on the selected daemon. Plans list them as SetOptionRequest actions with Global = true; Append applies them and Reuse leaves them untouched.
  • WorkspaceFile.GlobalOptions and WorkspaceWindow.OptionsAfter expose the same read-only maps in C#.

Commands: enter

windows:
  - panes:
      - enter: false
        shell_command_before:
          - "echo "
        shell_command:
          - cmd: ready
            enter: true
  • environment merges at session, window, and pane level; a child wins on the same key.
  • Commands are sent in session-before, window-before, pane-before, then pane order.
  • With enter: false, tmux types the text without pressing Enter, and later text joins the pending line until an Enter is requested. WorkspacePane.Enter holds the pane default.
  • A non-Boolean enter value is rejected with its path and location. Other tmuxp command modifiers are unsupported.

Choose startup and existing-session behavior

WorkspacePlanOptions: what the plan does about conflicts and readiness

Policy Behavior
ExistingSession = Error (default) Refuse a conflicting session.
ExistingSession = Reuse Return the inspected session without changing it.
ExistingSession = Append Add windows while preserving existing children and local session options; apply declared global options.
ExistingSession = Replace Replace the inspected session while preserving its daemon.
Readiness = Immediate (default) Send each command as literal input, pressing Enter according to its effective inherited state.
Readiness = Cooperative Wait for startup to signal its per-pane channel before sending commands.
  • The default ServerStartup = CreateOrJoin may start a daemon or join one that appeared after planning, and grants no ownership of it. RequireExisting needs a running daemon.
  • With Cooperative, each pane gets a fresh LIBTMUX_WORKSPACE_READY value, and startup signals it with tmux wait-for -S "$LIBTMUX_WORKSPACE_READY". A signal sent before the wait is kept.

This receiver signals before it starts /bin/cat, which accepts queued input:

string tmux = "'" + server.ConnectionOptions.TmuxBinaryPath
    .Replace("'", "'\"'\"'", StringComparison.Ordinal) + "'";
WorkspaceFile receiver = new("receiver",
    options: new Dictionary<string, string>
    {
        ["default-command"] = $"{tmux} wait-for -S \"$LIBTMUX_WORKSPACE_READY\"; exec /bin/cat",
    },
    windows: [new WorkspaceWindow("input", panes: [new WorkspacePane(["first line"])])]);
WorkspaceBuilder builder = new(server);
WorkspacePlan plan = await builder.PlanAsync(receiver, new WorkspacePlanOptions
{
    Readiness = WorkspaceReadiness.Cooperative,
    ReadinessTimeout = TimeSpan.FromSeconds(5),
}, ct);
WorkspaceResult result = await builder.ApplyAsync(plan, ct);
Console.WriteLine(result.Session.Name);

It prints the session name. A timeout stops input to that pane.

Handle results and failures

WorkspaceBuildException: what happened when application fails

  • BuildAsync returns the session and windows it created. Its Unsupported list names the final layouts tmux rejected; those windows stay usable.
  • Journal records every action, including failures and actions never started. PartialResult holds the state that exists, or is null when no session could be read.
  • Cancelling during application throws WorkspaceOperationCanceledException with the same partial state. It does not roll anything back.
  • CompensateOnFailure = true cleans up resources proven to be created by this application, within CleanupTimeout. It does not reverse shell commands, host effects, or global option changes.
  • A missing session name or an empty window list throws WorkspaceFormatException before anything is created.

Set window_index in YAML, or WorkspaceWindow(windowIndex: 5) in C#, to ask for an index. Duplicate requests fail local validation.

Library or command line

tmux-workspace, the command-line tool, has its own builder rather than calling WorkspaceBuilder. Read a file against the side that will build it:

This library tmux-workspace
Document language a closed tmuxp subset the tmuxp language, plus x- passthrough
Layout validation syntax and version checked before actions; runtime rejection recorded in Unsupported refuses the load
Shell readiness immediate by default; explicit cooperative readiness fails on timeout cursor heuristic; sends the command after timeout
A failure partway throws with an action journal and optional receipt-proven compensation removes a session it created
Session size tmux's default, 80x24 the invoking terminal

A file the CLI loads can raise WorkspaceFormatException here. Both build the same topology for the subset they share.

Package Adds
LibTmux The client. Required.
LibTmux.Query.Json JSON for query documents
LibTmux.Mcp A Model Context Protocol server, as a .NET tool

What it leaves out

  • A tmuxp runtime. Plugins, lifecycle hooks, and tmuxp's configuration search path are rejected. Run tmuxp for those.
  • The rest of tmuxp's language. It reads a closed subset: session name, start directory, options, global_options, options_after, windows, window indexes, panes, layouts, focus, environment, and shell_command values.
  • Host scripts by default. before_script runs only when the plan enables AllowHostScripts. It is /bin/sh -c text, bounded by HostScriptTimeout and MaxHostOutputBytes.
  • Process checkpoints. A captured declaration cannot recover the shell command a foreground program started from.

Support and status

  • Releases. 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.
  • Dependencies. LibTmux, and YamlDotNet for the files.
  • tmux and .NET. The tmux versions LibTmux supports, on net8.0 and net10.0.
  • Systems. Linux, including WSL. macOS is best effort, without CI coverage. CI builds on Windows x64.

Documentation

License

MIT

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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.1.1 232 11/1/2026
0.1.0 296 10/25/2026
0.0.0-alpha.24 97 10/25/2026
0.0.0-alpha.23 117 10/24/2026
0.0.0-alpha.22 131 10/19/2026
0.0.0-alpha.21 66 10/11/2026
0.0.0-alpha.20 417 10/5/2026
0.0.0-alpha.19 65 10/4/2026
0.0.0-alpha.18 68 10/3/2026
0.0.0-alpha.17 77 9/27/2026
0.0.0-alpha.16 69 9/26/2026
0.0.0-alpha.15 77 9/20/2026
0.0.0-alpha.14 69 9/20/2026
0.0.0-alpha.12 71 9/12/2026
0.0.0-alpha.10 88 8/30/2026
0.0.0-alpha.9 84 8/22/2026
0.0.0-alpha.8 89 8/22/2026
0.0.0-alpha.7 99 8/16/2026
0.0.0-alpha.6 84 8/16/2026
0.0.0-alpha.5 79 8/16/2026
Loading failed

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 any release may change it; pin an exact version.