diff --git a/WRITING.md b/WRITING.md
index 2a13cd89..f7940234 100644
--- a/WRITING.md
+++ b/WRITING.md
@@ -108,7 +108,8 @@ the copied example. Preserve collected examples when changing formatting.
Repeat a complete capture example with its native tools and tmux on `PATH`:
```console
-$ python3 scripts/check-capture-prose.py \
+$ python3 scripts/check-example-prose.py \
+ --example capture \
--port kotlin \
--output-dir /tmp/capture-kotlin-proof
```
@@ -117,10 +118,12 @@ The output directory must be new. The runner extracts the displayed files,
checks their recorded hashes, executes the displayed setup, and saves logs
and a result. Dependency downloads and native builds are separate from the
routine site tests. A changed hash needs a new native run before review.
+Use `--example attach` to check the existing-server programs in the attach guide.
Put explanatory comments on separate lines above the code they describe.
-Wrap example comments at 80 columns, including indentation. Put long source
-links and attribution in prose outside the code block.
+Limit example comments to 100 columns, including indentation; prefer shorter
+lines that fit the code panel. Put long source links and attribution in prose
+outside the code block.
### Examples across ports
diff --git a/scripts/check-capture-prose.py b/scripts/check-example-prose.py
similarity index 86%
rename from scripts/check-capture-prose.py
rename to scripts/check-example-prose.py
index 450b2f3e..c63c8127 100644
--- a/scripts/check-capture-prose.py
+++ b/scripts/check-example-prose.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-"""Run a complete capture example exactly as displayed, including its setup.
+"""Run a complete example exactly as displayed, including its setup.
Use --port to choose a language and --output-dir for a new evidence directory.
The selected language's native tools, Git, and tmux must already be on PATH.
@@ -18,12 +18,15 @@
def main():
repo = Path(__file__).resolve().parent.parent
- manifest = json.loads((repo / 'site/test/fixtures/capture-examples.json').read_text())
- examples = {item['port']: item for item in manifest['examples']}
parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument('--port', required=True, choices=examples)
+ parser.add_argument('--example', choices=['capture', 'attach'], default='capture')
+ parser.add_argument('--port', required=True)
parser.add_argument('--output-dir', required=True, type=Path)
args = parser.parse_args()
+ manifest = json.loads((repo / f'site/test/fixtures/{args.example}-examples.json').read_text())
+ examples = {item['port']: item for item in manifest['examples']}
+ if args.port not in examples:
+ parser.error(f'Unknown port: {args.port}; choose from {", ".join(examples)}')
example = examples[args.port]
page = repo / 'site/src/content/docs' / (example['page'] + '.md')
content = page.read_text()
@@ -68,7 +71,7 @@ def main():
if result.returncode:
break
passed = all(row['exit'] == 0 for row in results)
- passed = passed and 'libtmux capture ready' in log.read_text().splitlines()
+ passed = passed and example.get('expectedOutput', 'libtmux capture ready') in log.read_text().splitlines()
report = {'port': args.port, 'sourceRevision': example['sourceRevision'],
'pageSha256': hashlib.sha256(page.read_bytes()).hexdigest(),
'files': example['files'], 'runs': results, 'passed': passed,
diff --git a/site/src/content/docs/guides/attaching-to-tmux.md b/site/src/content/docs/guides/attaching-to-tmux.md
index e3e3d53e..46077014 100644
--- a/site/src/content/docs/guides/attaching-to-tmux.md
+++ b/site/src/content/docs/guides/attaching-to-tmux.md
@@ -1,7 +1,7 @@
---
-supportedPorts: [py, ts, rs, go, java, dotnet, cxx, swift]
+supportedPorts: []
title: Attaching to tmux
-description: What a plain constructor call actually connects to, and how to find a session that might already exist instead of always creating a new one.
+description: Create or attach to a tmux session, select its socket, and keep terminal attachment separate from automation.
sidebar:
label: Attaching to tmux
group: Guides
@@ -9,160 +9,136 @@ sidebar:
tableOfContents: true
---
-Obtain a server and session handle to control tmux from your program. Your
-process keeps its own stdin and stdout, and tmux continues running
-independently. [Attach and send keys](/examples/attach-and-send-keys/)
-demonstrates this workflow.
-
-
-Attaching your terminal is a separate operation. Python's `Session.attach()`
-runs `tmux attach-session` and hands the terminal to tmux.
-[tmuxp](https://tmuxp.git-pull.com/) uses it after building a workspace. Check
-your port's reference if your program needs to hand over the terminal.
-
-
-## Which socket a bare constructor reaches
-
-Use an explicit socket when several tmux servers may be running. The examples
-below show each constructor's defaults and environment-aware alternatives. To
-locate the server from inside a pane, use the port's environment lookup API for
-`TMUX` and `TMUX_PANE`.
-
-```python
-# Server() with no arguments talks to tmux's own default socket.
-# Server(socket_name=...) or Server(socket_path=...) pick a different one.
-server = libtmux.Server()
-
-# from_env() is also on Session, Window, and Pane, for code running inside a
-# pane that wants to ask "where am I" instead of being told.
-server = libtmux.Server.from_env()
-```
+Attaching opens a tmux session in your terminal. Its shells and programs keep
+running when you detach. A script can also query or control that session
+without taking over a terminal.
-```typescript
-// Select the default tmux socket.
-const server = new Server();
-```
+## Open a session in your terminal
-```go
-// Resolves the tmux binary once through the snapshotted PATH and freezes
-// it. SocketName and SocketPath select a socket explicitly (SocketPath wins
-// if both are set); a Server built this way does not drift if the
-// environment changes later.
-server, err := tmux.NewServer(tmux.ServerOptions{})
-if err != nil {
- return err
-}
-```
+Run this from a terminal outside tmux. It creates `work` if needed and attaches
+to it otherwise. `-L libtmux-demo` keeps this demonstration on its own named
+server. `-f /dev/null` skips personal tmux configuration for this demonstration.
-```rust
-// Server::new() uses the process's own environment. from_env() reads the
-// TMUX variable directly; find.rs tries the pane-local one first and falls
-// back to a fresh connection.
-let server = Server::from_env().or_else(|_| Server::new())?;
+```console
+$ tmux -L libtmux-demo -f /dev/null new-session -A -s work
```
-```java
-Server server = Server.open(
- ServerConfig.builder().endpoint(ServerEndpoint.socketPath(socket)).build());
+Detach with **Ctrl-b**, then **d**. You return to the original shell while
+the tmux session keeps running. List that server's sessions:
-// The pane-local read-back: takes nothing, returns empty outside a pane.
-TmuxEnvironment.current();
+```console
+$ tmux -L libtmux-demo list-sessions
```
-```csharp
-// Resolves in a fixed order: an explicit ServerConnectionOptions, then
-// LIBTMUX_SOCKET_PATH, then LIBTMUX_SOCKET_NAME (under TMUX_TMPDIR, or
-// /tmp), then the socket named "default". A named option always wins over
-// an environment variable.
-Server server = await Server.ConnectAsync();
-
-// The separate pane-local read-back; ConnectAsync never consults TMUX.
-Server fromPane = Server.FromEnvironment();
-```
-
-```cpp
-// Four named constructors instead of one flexible one: pick the one that
-// names how you're reaching this tmux:
-libtmux::Server::from_env(); // inside tmux
-libtmux::Server::at_socket_name(name);
-libtmux::Server::at_socket_path(path);
-libtmux::Server::at_default(); // "my tmux", to a person
-```
+Attach to the existing session again. The leading `=` selects its exact name:
-```swift
-// Select the default tmux socket explicitly.
-let server = try Server(socketName: "default")
+```console
+$ tmux -L libtmux-demo attach-session -t '=work'
```
-[Socket and servers](/topics/socket-and-servers/) covers endpoint selection
-and liveness. [Environment](/topics/environment/) covers pane-local lookup.
+`attach-session` expects a session to exist. `new-session -A` is the command
+to use when either creating or attaching is acceptable. From inside tmux,
+`attach-session` switches the attached client to the target session.
-## Finding a session instead of always creating one
+After detaching, remove the demonstration session when you are finished:
-A script that runs more than once usually wants "attach if a session by
-this name already exists, create it otherwise," not a fresh session every
-time.
-
-```python
-# default only stands in for *absence*: an ambiguous match still raises
-# MultipleObjectsReturned even with a default supplied: handing back an
-# arbitrary match from several is how a script ends up driving the wrong
-# pane. See Filtering and queries.
-session = server.sessions.get(session_name="demo", default=None)
-if session is None:
- session = server.new_session(session_name="demo")
+```console
+$ tmux -L libtmux-demo kill-session -t '=work'
```
-```typescript
-const session = snapshot.sessions.where({ name: "demo" }).oneOrUndefined();
+
+
+## Choose the server socket
+
+A socket identifies a tmux server. Use the same selection on every command:
+
+- `-L name` selects a named socket in tmux's socket directory.
+- `-S path` selects an explicit socket path and overrides `-L`.
+- Without either flag, tmux uses the socket from `TMUX` when applicable,
+ otherwise its default socket.
+
+Inside a pane, `TMUX` identifies its server and `TMUX_PANE` identifies the
+pane. Prefer explicit socket selection in automation that may run both
+inside and outside tmux.
+
+## Query a session from a shell script
+
+This complete program creates an isolated server, looks up `work`, and prints
+its name. Each command stays in the calling shell; no terminal is attached.
+It stops its own server on success or failure.
+
+Save it as `connect.sh` and run `sh connect.sh`, or paste the whole block into
+a POSIX shell. It requires tmux 3.2a or newer.
+
+```sh title="connect.sh"
+(
+ set -eu
+ directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-attach.XXXXXX")
+ socket="$directory/tmux.sock"
+
+ cleanup() {
+ status=$?
+ trap - 0 HUP INT TERM
+ if [ -S "$socket" ] && ! tmux -S "$socket" kill-server; then
+ printf 'Cannot stop tmux; kept %s\n' "$directory" >&2
+ exit 1
+ fi
+ rm -rf "$directory" || exit 1
+ exit "$status"
+ }
+ trap cleanup 0
+ trap 'exit 1' HUP INT TERM
+
+ unset TMUX TMUX_PANE
+ tmux -S "$socket" -f /dev/null new-session -d -s work /bin/cat
+ tmux -S "$socket" has-session -t '=work'
+ tmux -S "$socket" list-sessions -F '#{session_name}'
+)
```
-```go
-// Push the check into tmux itself with a typed filter, rather than reading
-// everything back and filtering in the process.
-live := tmux.TmuxFilter("#{==:#{session_name},demo}")
-sessions, err := server.SearchSessions(ctx, &live)
-if err != nil {
- return err
-}
-fmt.Println("matching sessions:", len(sessions))
-```
+`-d` starts the session without attaching. `/bin/cat` keeps its pane open
+without loading a shell configuration. The script addresses only its private
+socket. If shutdown fails, it reports the error and keeps that socket's
+directory for inspection.
-```java
-Session session = server.hasSession("work")
- ? server.sessions().stream()
- .filter(candidate -> candidate.name().equals("work"))
- .findFirst()
- .orElseThrow()
- : server.newSession("work");
-```
+
-```swift
-// hasSession answers the question directly as a Bool, no exception needed
-// either way.
-if try await server.hasSession("work") == false {
- _ = try await server.newSession(named: "work", windowName: "start")
-}
-```
+## Find a session before creating one
-
-`Server.HasSessionAsync(name)` checks for a session. Use `NewSessionRequest.ReplaceExisting`
-with `Server.CreateSessionAsync` only when killing and recreating it is intended.
-
+Use `has-session -t '=name'` to check an exact session name. It exits
+unsuccessfully when tmux cannot find the session or contact the server; keep
+the diagnostic so you can distinguish those failures.
-Finding an object and creating one are separate operations. Another client
-can change tmux state between them. Handle the creation error if the name was
-taken after the lookup.
+A lookup does not reserve a name. Another client can create or remove a
+session before your next command. Handle the result of `new-session` even
+after checking. Use `new-session -A` for the interactive create-or-attach
+workflow shown above.
-[Filtering and querying](../querying-and-filtering/) covers absent and
-ambiguous matches. [Attach and send keys](/examples/attach-and-send-keys/)
-contains complete programs and their source details.
+## Connect from a language library
+
+Use the port menu to open this guide with a complete program, imports, build
+files, and a private-server launcher. Each program connects to an existing
+socket and leaves that server running:
+
+[Python](/py/latest/guides/attaching-to-tmux/) ·
+[TypeScript](/ts/latest/guides/attaching-to-tmux/) ·
+[Go](/go/latest/guides/attaching-to-tmux/) ·
+[Rust](/rs/latest/guides/attaching-to-tmux/) ·
+[Java](/java/latest/guides/attaching-to-tmux/) ·
+[Kotlin](/kotlin/latest/guides/attaching-to-tmux/) ·
+[Scala](/scala/latest/guides/attaching-to-tmux/) ·
+[.NET](/dotnet/latest/guides/attaching-to-tmux/) ·
+[F#](/fsharp/latest/guides/attaching-to-tmux/) ·
+[C++](/cxx/latest/guides/attaching-to-tmux/) ·
+[Swift](/swift/latest/guides/attaching-to-tmux/) ·
+[Ruby](/ruby/latest/guides/attaching-to-tmux/) ·
+[Lua](/lua/latest/guides/attaching-to-tmux/)
## Where to go next
-- [Sending keys](../sending-keys/) and [Capturing output](../capturing-output/)
- pick up once you have a pane handle.
-- [Attach and send keys](/examples/attach-and-send-keys/) has the full,
- sourced code for the round trip this guide assumes.
-- [Testing with libtmux](../testing-with-libtmux/) if the server you want to
- attach to is one your own test suite should own and tear down.
+- [Sending keys](../sending-keys/) explains input and command completion.
+- [Capturing output](../capturing-output/) reads a pane's screen and history.
+- [Socket and servers](/topics/socket-and-servers/) covers server selection.
+
+The [tmux manual source](https://github.com/tmux/tmux/blob/94796f6b1182507efac8a272fc309a79e22e58a5/tmux.1)
+describes `new-session`, `attach-session`, socket selection, and targeting.
diff --git a/site/src/content/docs/ports/cxx/examples/capture-pane-output.md b/site/src/content/docs/ports/cxx/examples/capture-pane-output.md
index ec60ae7b..75d0ba8c 100644
--- a/site/src/content/docs/ports/cxx/examples/capture-pane-output.md
+++ b/site/src/content/docs/ports/cxx/examples/capture-pane-output.md
@@ -18,6 +18,10 @@ This complete program creates a private tmux server, captures its output, and
cleans up. Follow the [setup and run instructions](#setup-and-run) below. You need
tmux and a Unix environment; no existing tmux session is required.
+`ScopedTmuxServer` is temporary example scaffolding. In an application, use a
+[`Server`](../../reference/libtmux-server/) connected to the tmux server you manage.
+Future versions of this example will use that regular server object directly.
+
## Read what's on screen
The program sends `printf` with a leading newline, then waits for the complete
diff --git a/site/src/content/docs/ports/cxx/guides/attaching-to-tmux.md b/site/src/content/docs/ports/cxx/guides/attaching-to-tmux.md
new file mode 100644
index 00000000..69c9caf7
--- /dev/null
+++ b/site/src/content/docs/ports/cxx/guides/attaching-to-tmux.md
@@ -0,0 +1,140 @@
+---
+port: cxx
+route: guides/attaching-to-tmux
+title: Attaching to tmux
+description: Connect to an existing tmux server and find a session with C++.
+sidebar:
+ label: Attaching to tmux
+ group: Guides
+ order: 3
+tableOfContents: true
+---
+
+Connect a `Server` to an explicit socket and find the existing `work` session.
+The program prints its name and leaves the tmux server running. It reports an
+error if the connection fails or the session is absent.
+
+This controls tmux from your program. To open a session in your terminal, use
+`tmux attach-session`; the [shared guide](../../../../guides/attaching-to-tmux/) covers
+interactive attachment and detaching.
+
+
+
+## Connect to an existing server
+
+Save the complete program as `connect.cpp`. `LIBTMUX_SOCKET_PATH` selects
+the existing server. The launcher below supplies a private socket for trying
+the example.
+
+```cpp title="connect.cpp"
+#include
+#include
+#include
+
+#include
+
+int main() {
+ try {
+ const char* socket = std::getenv("LIBTMUX_SOCKET_PATH");
+ if (!socket) throw std::runtime_error("Set LIBTMUX_SOCKET_PATH to an existing socket");
+ auto server = libtmux::Server::at_socket_path(socket);
+ if (!server) throw std::runtime_error(server.error().diagnostic);
+ auto sessions = server->sessions();
+ if (!sessions) throw std::runtime_error(sessions.error().diagnostic);
+ for (const auto& session : *sessions) {
+ if (session.name() == "work") {
+ std::cout << "work\n";
+ return 0;
+ }
+ }
+ throw std::runtime_error("The work session does not exist");
+ } catch (const std::exception& error) {
+ std::cerr << error.what() << '\n';
+ return 1;
+ }
+}
+```
+
+## Setup and run
+
+Use an empty directory on Linux with Git and tmux 3.2a or newer installed.
+
+This example was checked with Clang 18, libc++ 18, CMake 3.25+.
+
+Use CMake 3.25 or newer, Ninja, and Clang 18 with libc++ 18.
+
+Save this file beside the program using the displayed filename.
+
+```cmake title="CMakeLists.txt"
+cmake_minimum_required(VERSION 3.25)
+project(connect_example LANGUAGES CXX)
+set(LIBTMUX_BUILD_TESTS OFF CACHE BOOL "" FORCE)
+set(LIBTMUX_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
+add_subdirectory(libtmux-source)
+add_executable(connect connect.cpp)
+target_compile_features(connect PRIVATE cxx_std_23)
+target_link_libraries(connect PRIVATE libtmux::libtmux)
+```
+
+Save the launcher as `run.sh`. It starts an isolated tmux server, runs the
+program, checks that the session still exists, then stops only that server.
+Cleanup runs after failures too. A failed shutdown keeps its socket directory
+and prints its location for inspection.
+
+```sh title="run.sh"
+#!/bin/sh
+set -eu
+
+binary=$(command -v tmux)
+directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-cxx-attach.XXXXXX")
+socket="$directory/tmux.sock"
+
+cleanup() {
+ status=$?
+ trap - 0 HUP INT TERM
+ if [ -S "$socket" ] && ! "$binary" -S "$socket" kill-server; then
+ printf 'Cannot stop tmux; kept %s\n' "$directory" >&2
+ exit 1
+ fi
+ rm -rf "$directory" || exit 1
+ exit "$status"
+}
+trap cleanup 0
+trap 'exit 1' HUP INT TERM
+
+unset TMUX TMUX_PANE
+export LIBTMUX_SOCKET_PATH="$socket" TMUX_BIN="$binary"
+"$binary" -S "$socket" -f /dev/null new-session -d -s work /bin/cat
+"$@"
+"$binary" -S "$socket" has-session -t '=work'
+```
+
+Fetch the verified library revision, build, and run:
+
+```console
+$ git clone https://github.com/libtmux/libtmux-cxx libtmux-source &&
+ git -C libtmux-source checkout 393d4b0ad666f18a6581f1eb281741a75a7503f0 &&
+ cmake -S . -B build -G Ninja \
+ -DCMAKE_CXX_COMPILER=clang++ \
+ -DCMAKE_CXX_FLAGS=-stdlib=libc++ \
+ -DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++ &&
+ cmake --build build --target connect --parallel 2 &&
+ sh run.sh ./build/connect
+```
+
+The program prints `work`. To use an existing server of your own, set
+`LIBTMUX_SOCKET_PATH` to its socket and run the program without the launcher.
+That launcher is responsible for the demonstration server's lifetime.
+
+
+
+## Find or create a session
+
+The example only looks up a session. If your application creates a session
+after an unsuccessful lookup, another client may create the same name between
+those operations. Handle the creation error instead of assuming the lookup
+reserves the name.
+
+For a complete program that starts and owns its server, see
+[Capture pane output](/examples/capture-pane-output/). Continue with
+[Sending keys](/guides/sending-keys/) once you have a pane handle.
diff --git a/site/src/content/docs/ports/cxx/workspace/internals/examples.md b/site/src/content/docs/ports/cxx/workspace/internals/examples.md
index 8ee1cc5f..cfa208c6 100644
--- a/site/src/content/docs/ports/cxx/workspace/internals/examples.md
+++ b/site/src/content/docs/ports/cxx/workspace/internals/examples.md
@@ -32,6 +32,10 @@ Save the following build file. The typed builder is a source header; this exampl
does not use the optional YAML reader. The public testing library supplies
private-server startup and cleanup.
+`ScopedTmuxServer` is temporary example scaffolding. In an application, use a
+[`Server`](../../../reference/libtmux-server/) connected to the tmux server you manage.
+Future versions of this example will use that regular server object directly.
+
```cmake title="CMakeLists.txt"
cmake_minimum_required(VERSION 3.25)
project(workspace_example LANGUAGES CXX)
diff --git a/site/src/content/docs/ports/dotnet/guides/attaching-to-tmux.md b/site/src/content/docs/ports/dotnet/guides/attaching-to-tmux.md
new file mode 100644
index 00000000..749819c9
--- /dev/null
+++ b/site/src/content/docs/ports/dotnet/guides/attaching-to-tmux.md
@@ -0,0 +1,126 @@
+---
+port: dotnet
+route: guides/attaching-to-tmux
+title: Attaching to tmux
+description: Connect to an existing tmux server and find a session with .NET.
+sidebar:
+ label: Attaching to tmux
+ group: Guides
+ order: 3
+tableOfContents: true
+---
+
+Connect a `Server` to an explicit socket and find the existing `work` session.
+The program prints its name and leaves the tmux server running. It reports an
+error if the connection fails or the session is absent.
+
+This controls tmux from your program. To open a session in your terminal, use
+`tmux attach-session`; the [shared guide](../../../../guides/attaching-to-tmux/) covers
+interactive attachment and detaching.
+
+
+
+## Connect to an existing server
+
+Save the complete program as `Program.cs`. `LIBTMUX_SOCKET_PATH` selects
+the existing server. The launcher below supplies a private socket for trying
+the example.
+
+```csharp title="Program.cs"
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using LibTmux;
+
+string socket = Environment.GetEnvironmentVariable("LIBTMUX_SOCKET_PATH")
+ ?? throw new InvalidOperationException("Set LIBTMUX_SOCKET_PATH to an existing socket");
+using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
+Server server = await Server.ConnectAsync(
+ new ServerConnectionOptions { SocketPath = socket }, timeout.Token);
+if (!await server.HasSessionAsync("work", cancellationToken: timeout.Token))
+ throw new InvalidOperationException("The work session does not exist");
+Console.WriteLine("work");
+```
+
+## Setup and run
+
+Use an empty directory on Linux with Git and tmux 3.2a or newer installed.
+
+This example was checked with .NET SDK 10.0.302.
+
+Save this file beside the program using the displayed filename.
+
+```xml title="Connect.csproj"
+
+
+ Exe
+ net10.0
+ disable
+ enable
+ false
+
+
+
+
+
+
+```
+
+Save the launcher as `run.sh`. It starts an isolated tmux server, runs the
+program, checks that the session still exists, then stops only that server.
+Cleanup runs after failures too. A failed shutdown keeps its socket directory
+and prints its location for inspection.
+
+```sh title="run.sh"
+#!/bin/sh
+set -eu
+
+binary=$(command -v tmux)
+directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-dotnet-attach.XXXXXX")
+socket="$directory/tmux.sock"
+
+cleanup() {
+ status=$?
+ trap - 0 HUP INT TERM
+ if [ -S "$socket" ] && ! "$binary" -S "$socket" kill-server; then
+ printf 'Cannot stop tmux; kept %s\n' "$directory" >&2
+ exit 1
+ fi
+ rm -rf "$directory" || exit 1
+ exit "$status"
+}
+trap cleanup 0
+trap 'exit 1' HUP INT TERM
+
+unset TMUX TMUX_PANE
+export LIBTMUX_SOCKET_PATH="$socket" TMUX_BIN="$binary"
+"$binary" -S "$socket" -f /dev/null new-session -d -s work /bin/cat
+"$@"
+"$binary" -S "$socket" has-session -t '=work'
+```
+
+Fetch the verified library revision, build, and run:
+
+```console
+$ git clone https://github.com/libtmux/libtmux-dotnet libtmux-source &&
+ git -C libtmux-source checkout 320dc64f4b8b7815842471327a5e6b84a1499bf8 &&
+ dotnet build Connect.csproj --maxcpucount:1 &&
+ sh run.sh dotnet run --project Connect.csproj --no-build
+```
+
+The program prints `work`. To use an existing server of your own, set
+`LIBTMUX_SOCKET_PATH` to its socket and run the program without the launcher.
+That launcher is responsible for the demonstration server's lifetime.
+
+
+
+## Find or create a session
+
+The example only looks up a session. If your application creates a session
+after an unsuccessful lookup, another client may create the same name between
+those operations. Handle the creation error instead of assuming the lookup
+reserves the name.
+
+For a complete program that starts and owns its server, see
+[Capture pane output](/examples/capture-pane-output/). Continue with
+[Sending keys](/guides/sending-keys/) once you have a pane handle.
diff --git a/site/src/content/docs/ports/fsharp/guides/attaching-to-tmux.md b/site/src/content/docs/ports/fsharp/guides/attaching-to-tmux.md
new file mode 100644
index 00000000..d3cb6038
--- /dev/null
+++ b/site/src/content/docs/ports/fsharp/guides/attaching-to-tmux.md
@@ -0,0 +1,136 @@
+---
+port: fsharp
+route: guides/attaching-to-tmux
+title: Attaching to tmux
+description: Connect to an existing tmux server and find a session with F#.
+sidebar:
+ label: Attaching to tmux
+ group: Guides
+ order: 3
+tableOfContents: true
+---
+
+Connect a `Server` to an explicit socket and find the existing `work` session.
+The program prints its name and leaves the tmux server running. It reports an
+error if the connection fails or the session is absent.
+
+This controls tmux from your program. To open a session in your terminal, use
+`tmux attach-session`; the [shared guide](../../../../guides/attaching-to-tmux/) covers
+interactive attachment and detaching.
+
+
+
+## Connect to an existing server
+
+Save the complete program as `Program.fs`. `LIBTMUX_SOCKET_PATH` selects
+the existing server. The launcher below supplies a private socket for trying
+the example.
+
+```fsharp title="Program.fs"
+open System
+open System.Threading
+open System.Threading.Tasks
+open LibTmux
+
+let connect () = task {
+ let socket = Environment.GetEnvironmentVariable("LIBTMUX_SOCKET_PATH")
+ if String.IsNullOrEmpty(socket) then
+ invalidOp "Set LIBTMUX_SOCKET_PATH to an existing socket"
+ use timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5.0))
+ let! server = Server.ConnectAsync(
+ ServerConnectionOptions(SocketPath = socket), timeout.Token)
+ let! exists = server.HasSessionAsync("work", cancellationToken = timeout.Token)
+ if not exists then invalidOp "The work session does not exist"
+ printfn "work"
+}
+
+[]
+let main _ =
+ try
+ connect().GetAwaiter().GetResult()
+ 0
+ with error ->
+ eprintfn "%O" error
+ 1
+```
+
+## Setup and run
+
+Use an empty directory on Linux with Git and tmux 3.2a or newer installed.
+
+This example was checked with .NET SDK 10.0.302.
+
+Save this file beside the program using the displayed filename.
+
+```xml title="Connect.fsproj"
+
+
+ Exe
+ net10.0
+
+
+
+
+
+
+```
+
+Save the launcher as `run.sh`. It starts an isolated tmux server, runs the
+program, checks that the session still exists, then stops only that server.
+Cleanup runs after failures too. A failed shutdown keeps its socket directory
+and prints its location for inspection.
+
+```sh title="run.sh"
+#!/bin/sh
+set -eu
+
+binary=$(command -v tmux)
+directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-fsharp-attach.XXXXXX")
+socket="$directory/tmux.sock"
+
+cleanup() {
+ status=$?
+ trap - 0 HUP INT TERM
+ if [ -S "$socket" ] && ! "$binary" -S "$socket" kill-server; then
+ printf 'Cannot stop tmux; kept %s\n' "$directory" >&2
+ exit 1
+ fi
+ rm -rf "$directory" || exit 1
+ exit "$status"
+}
+trap cleanup 0
+trap 'exit 1' HUP INT TERM
+
+unset TMUX TMUX_PANE
+export LIBTMUX_SOCKET_PATH="$socket" TMUX_BIN="$binary"
+"$binary" -S "$socket" -f /dev/null new-session -d -s work /bin/cat
+"$@"
+"$binary" -S "$socket" has-session -t '=work'
+```
+
+Fetch the verified library revision, build, and run:
+
+```console
+$ git clone https://github.com/libtmux/libtmux-dotnet libtmux-source &&
+ git -C libtmux-source checkout 661287848a6cfb407f37114b25e8249a29f99e3f &&
+ dotnet build Connect.fsproj --maxcpucount:1 \
+ -p:DisableImplicitLibraryPacksFolder=true \
+ -p:RestorePackagesPath="$PWD/.packages" &&
+ sh run.sh dotnet run --project Connect.fsproj --no-build
+```
+
+The program prints `work`. To use an existing server of your own, set
+`LIBTMUX_SOCKET_PATH` to its socket and run the program without the launcher.
+That launcher is responsible for the demonstration server's lifetime.
+
+
+
+## Find or create a session
+
+The example only looks up a session. If your application creates a session
+after an unsuccessful lookup, another client may create the same name between
+those operations. Handle the creation error instead of assuming the lookup
+reserves the name.
+
+For a complete program that starts and owns its server, see
+[Capture pane output](/examples/capture-pane-output/).
diff --git a/site/src/content/docs/ports/go/guides/attaching-to-tmux.md b/site/src/content/docs/ports/go/guides/attaching-to-tmux.md
new file mode 100644
index 00000000..3099c552
--- /dev/null
+++ b/site/src/content/docs/ports/go/guides/attaching-to-tmux.md
@@ -0,0 +1,140 @@
+---
+port: go
+route: guides/attaching-to-tmux
+title: Attaching to tmux
+description: Connect to an existing tmux server and find a session with Go.
+sidebar:
+ label: Attaching to tmux
+ group: Guides
+ order: 3
+tableOfContents: true
+---
+
+Connect a `Server` to an explicit socket and find the existing `work` session.
+The program prints its name and leaves the tmux server running. It reports an
+error if the connection fails or the session is absent.
+
+This controls tmux from your program. To open a session in your terminal, use
+`tmux attach-session`; the [shared guide](../../../../guides/attaching-to-tmux/) covers
+interactive attachment and detaching.
+
+
+
+## Connect to an existing server
+
+Save the complete program as `main.go`. `LIBTMUX_SOCKET_PATH` selects
+the existing server. The launcher below supplies a private socket for trying
+the example.
+
+```go title="main.go"
+package main
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "os"
+ "time"
+
+ "github.com/libtmux/libtmux-go/tmux"
+)
+
+func main() {
+ socket := os.Getenv("LIBTMUX_SOCKET_PATH")
+ if socket == "" {
+ log.Fatal("Set LIBTMUX_SOCKET_PATH to an existing socket")
+ }
+ server, err := tmux.NewServer(tmux.ServerOptions{SocketPath: socket})
+ if err != nil {
+ log.Fatal(err)
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ filter := tmux.TmuxFilter("#{==:#{session_name},work}")
+ sessions, err := server.SearchSessions(ctx, &filter)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if len(sessions) != 1 {
+ log.Fatalf("Expected one work session, found %d", len(sessions))
+ }
+ fmt.Println("work")
+}
+```
+
+## Setup and run
+
+Use an empty directory on Linux with Git and tmux 3.2a or newer installed.
+
+This example was checked with Go 1.26.8.
+
+Save this file beside the program using the displayed filename.
+
+```text title="go.mod"
+module example.com/connect
+
+go 1.26.0
+
+require github.com/libtmux/libtmux-go v0.0.0
+
+replace github.com/libtmux/libtmux-go => ./libtmux
+```
+
+Save the launcher as `run.sh`. It starts an isolated tmux server, runs the
+program, checks that the session still exists, then stops only that server.
+Cleanup runs after failures too. A failed shutdown keeps its socket directory
+and prints its location for inspection.
+
+```sh title="run.sh"
+#!/bin/sh
+set -eu
+
+binary=$(command -v tmux)
+directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-go-attach.XXXXXX")
+socket="$directory/tmux.sock"
+
+cleanup() {
+ status=$?
+ trap - 0 HUP INT TERM
+ if [ -S "$socket" ] && ! "$binary" -S "$socket" kill-server; then
+ printf 'Cannot stop tmux; kept %s\n' "$directory" >&2
+ exit 1
+ fi
+ rm -rf "$directory" || exit 1
+ exit "$status"
+}
+trap cleanup 0
+trap 'exit 1' HUP INT TERM
+
+unset TMUX TMUX_PANE
+export LIBTMUX_SOCKET_PATH="$socket" TMUX_BIN="$binary"
+"$binary" -S "$socket" -f /dev/null new-session -d -s work /bin/cat
+"$@"
+"$binary" -S "$socket" has-session -t '=work'
+```
+
+Fetch the verified library revision, build, and run:
+
+```console
+$ git clone https://github.com/libtmux/libtmux-go libtmux &&
+ git -C libtmux checkout bb06e26e116e941813ca40bf45e7e3a47d38f52a &&
+ GOWORK=off go mod tidy &&
+ sh run.sh env GOWORK=off go run .
+```
+
+The program prints `work`. To use an existing server of your own, set
+`LIBTMUX_SOCKET_PATH` to its socket and run the program without the launcher.
+That launcher is responsible for the demonstration server's lifetime.
+
+
+
+## Find or create a session
+
+The example only looks up a session. If your application creates a session
+after an unsuccessful lookup, another client may create the same name between
+those operations. Handle the creation error instead of assuming the lookup
+reserves the name.
+
+For a complete program that starts and owns its server, see
+[Capture pane output](/examples/capture-pane-output/). Continue with
+[Sending keys](/guides/sending-keys/) once you have a pane handle.
diff --git a/site/src/content/docs/ports/java/guides/attaching-to-tmux.md b/site/src/content/docs/ports/java/guides/attaching-to-tmux.md
new file mode 100644
index 00000000..280152c9
--- /dev/null
+++ b/site/src/content/docs/ports/java/guides/attaching-to-tmux.md
@@ -0,0 +1,120 @@
+---
+port: java
+route: guides/attaching-to-tmux
+title: Attaching to tmux
+description: Connect to an existing tmux server and find a session with Java.
+sidebar:
+ label: Attaching to tmux
+ group: Guides
+ order: 3
+tableOfContents: true
+---
+
+Connect a `Server` to an explicit socket and find the existing `work` session.
+The program prints its name and leaves the tmux server running. It reports an
+error if the connection fails or the session is absent.
+
+This controls tmux from your program. To open a session in your terminal, use
+`tmux attach-session`; the [shared guide](../../../../guides/attaching-to-tmux/) covers
+interactive attachment and detaching.
+
+
+
+## Connect to an existing server
+
+Save the complete program as `Connect.java`. `LIBTMUX_SOCKET_PATH` selects
+the existing server. The launcher below supplies a private socket for trying
+the example.
+
+```java title="Connect.java"
+import io.github.libtmux.Server;
+import io.github.libtmux.ServerConfig;
+import io.github.libtmux.ServerEndpoint;
+import io.github.libtmux.Session;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.util.Objects;
+
+public final class Connect {
+ public static void main(String[] args) {
+ String socket = Objects.requireNonNull(System.getenv("LIBTMUX_SOCKET_PATH"),
+ "Set LIBTMUX_SOCKET_PATH to an existing socket");
+ ServerConfig config = ServerConfig.builder()
+ .endpoint(ServerEndpoint.socketPath(Path.of(socket)))
+ .defaultTimeout(Duration.ofSeconds(5))
+ .build();
+ try (Server server = Server.open(config)) {
+ Session session = server.sessions().stream()
+ .filter(candidate -> candidate.name().equals("work"))
+ .findFirst()
+ .orElseThrow(() -> new IllegalStateException("The work session does not exist"));
+ System.out.println(session.name());
+ }
+ }
+}
+```
+
+## Setup and run
+
+Use an empty directory on Linux with Git and tmux 3.2a or newer installed.
+
+This example was checked with JDK 25.0.3, library bytecode target Java 21.
+
+Save the launcher as `run.sh`. It starts an isolated tmux server, runs the
+program, checks that the session still exists, then stops only that server.
+Cleanup runs after failures too. A failed shutdown keeps its socket directory
+and prints its location for inspection.
+
+```sh title="run.sh"
+#!/bin/sh
+set -eu
+
+binary=$(command -v tmux)
+directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-java-attach.XXXXXX")
+socket="$directory/tmux.sock"
+
+cleanup() {
+ status=$?
+ trap - 0 HUP INT TERM
+ if [ -S "$socket" ] && ! "$binary" -S "$socket" kill-server; then
+ printf 'Cannot stop tmux; kept %s\n' "$directory" >&2
+ exit 1
+ fi
+ rm -rf "$directory" || exit 1
+ exit "$status"
+}
+trap cleanup 0
+trap 'exit 1' HUP INT TERM
+
+unset TMUX TMUX_PANE
+export LIBTMUX_SOCKET_PATH="$socket" TMUX_BIN="$binary"
+"$binary" -S "$socket" -f /dev/null new-session -d -s work /bin/cat
+"$@"
+"$binary" -S "$socket" has-session -t '=work'
+```
+
+Fetch the verified library revision, build, and run:
+
+```console
+$ git clone https://github.com/libtmux/libtmux-java libtmux &&
+ git -C libtmux checkout 842228310449e879ebcaa3f910597757c9dbffd6 &&
+ (cd libtmux && ./gradlew :libtmux:jar) &&
+ sh run.sh java --class-path 'libtmux/libtmux/build/libs/*' Connect.java
+```
+
+The program prints `work`. To use an existing server of your own, set
+`LIBTMUX_SOCKET_PATH` to its socket and run the program without the launcher.
+That launcher is responsible for the demonstration server's lifetime.
+
+
+
+## Find or create a session
+
+The example only looks up a session. If your application creates a session
+after an unsuccessful lookup, another client may create the same name between
+those operations. Handle the creation error instead of assuming the lookup
+reserves the name.
+
+For a complete program that starts and owns its server, see
+[Capture pane output](/examples/capture-pane-output/). Continue with
+[Sending keys](/guides/sending-keys/) once you have a pane handle.
diff --git a/site/src/content/docs/ports/kotlin/guides/attaching-to-tmux.md b/site/src/content/docs/ports/kotlin/guides/attaching-to-tmux.md
new file mode 100644
index 00000000..547f315f
--- /dev/null
+++ b/site/src/content/docs/ports/kotlin/guides/attaching-to-tmux.md
@@ -0,0 +1,148 @@
+---
+port: kotlin
+route: guides/attaching-to-tmux
+title: Attaching to tmux
+description: Connect to an existing tmux server and find a session with Kotlin.
+sidebar:
+ label: Attaching to tmux
+ group: Guides
+ order: 3
+tableOfContents: true
+---
+
+Connect a `Server` to an explicit socket and find the existing `work` session.
+The program prints its name and leaves the tmux server running. It reports an
+error if the connection fails or the session is absent.
+
+This controls tmux from your program. To open a session in your terminal, use
+`tmux attach-session`; the [shared guide](../../../../guides/attaching-to-tmux/) covers
+interactive attachment and detaching.
+
+
+
+## Connect to an existing server
+
+Save the complete program as `Connect.kt`. `LIBTMUX_SOCKET_PATH` selects
+the existing server. The launcher below supplies a private socket for trying
+the example.
+
+```kotlin title="Connect.kt"
+import io.github.libtmux.ServerConfig
+import io.github.libtmux.ServerEndpoint
+import io.github.libtmux.kotlin.sessions
+import io.github.libtmux.kotlin.withServer
+import java.nio.file.Path
+import java.time.Duration
+import kotlinx.coroutines.runBlocking
+
+fun main() = runBlocking {
+ val socket = requireNotNull(System.getenv("LIBTMUX_SOCKET_PATH")) {
+ "Set LIBTMUX_SOCKET_PATH to an existing socket"
+ }
+ val config = ServerConfig.builder()
+ .endpoint(ServerEndpoint.socketPath(Path.of(socket)))
+ .defaultTimeout(Duration.ofSeconds(5))
+ .build()
+ withServer(config) { server ->
+ val session = server.sessions().single { it.name == "work" }
+ println(session.name)
+ }
+}
+```
+
+## Setup and run
+
+Use an empty directory on Linux with Git and tmux 3.2a or newer installed.
+
+This example was checked with JDK 25.0.3, Kotlin 2.4.10.
+
+Save this file beside the program using the displayed filename.
+
+```kotlin title="build.gradle.kts"
+plugins {
+ application
+ kotlin("jvm") version "2.4.10"
+}
+
+repositories { mavenCentral() }
+dependencies {
+ implementation("io.github.libtmux:libtmux-kotlin:0.0.1-alpha.17-SNAPSHOT")
+}
+kotlin {
+ jvmToolchain(25)
+ sourceSets.main { kotlin.srcDir("."); kotlin.include("Connect.kt") }
+}
+application { mainClass.set("ConnectKt") }
+```
+
+Save this file beside the program using the displayed filename.
+
+```kotlin title="settings.gradle.kts"
+rootProject.name = "connect"
+includeBuild("libtmux-source")
+```
+
+Save this file beside the program using the displayed filename.
+
+```properties title="gradle.properties"
+org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8
+kotlin.daemon.jvmargs=-Xmx2g
+org.gradle.workers.max=2
+```
+
+Save the launcher as `run.sh`. It starts an isolated tmux server, runs the
+program, checks that the session still exists, then stops only that server.
+Cleanup runs after failures too. A failed shutdown keeps its socket directory
+and prints its location for inspection.
+
+```sh title="run.sh"
+#!/bin/sh
+set -eu
+
+binary=$(command -v tmux)
+directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-kotlin-attach.XXXXXX")
+socket="$directory/tmux.sock"
+
+cleanup() {
+ status=$?
+ trap - 0 HUP INT TERM
+ if [ -S "$socket" ] && ! "$binary" -S "$socket" kill-server; then
+ printf 'Cannot stop tmux; kept %s\n' "$directory" >&2
+ exit 1
+ fi
+ rm -rf "$directory" || exit 1
+ exit "$status"
+}
+trap cleanup 0
+trap 'exit 1' HUP INT TERM
+
+unset TMUX TMUX_PANE
+export LIBTMUX_SOCKET_PATH="$socket" TMUX_BIN="$binary"
+"$binary" -S "$socket" -f /dev/null new-session -d -s work /bin/cat
+"$@"
+"$binary" -S "$socket" has-session -t '=work'
+```
+
+Fetch the verified library revision, build, and run:
+
+```console
+$ git clone https://github.com/libtmux/libtmux-java libtmux-source &&
+ git -C libtmux-source checkout 85ebf6955e56703c5be74e2afd34a18309044741 &&
+ sh run.sh ./libtmux-source/gradlew --project-dir . run --console=plain --max-workers=2
+```
+
+The program prints `work`. To use an existing server of your own, set
+`LIBTMUX_SOCKET_PATH` to its socket and run the program without the launcher.
+That launcher is responsible for the demonstration server's lifetime.
+
+
+
+## Find or create a session
+
+The example only looks up a session. If your application creates a session
+after an unsuccessful lookup, another client may create the same name between
+those operations. Handle the creation error instead of assuming the lookup
+reserves the name.
+
+For a complete program that starts and owns its server, see
+[Capture pane output](/examples/capture-pane-output/).
diff --git a/site/src/content/docs/ports/lua/guides/attaching-to-tmux.md b/site/src/content/docs/ports/lua/guides/attaching-to-tmux.md
new file mode 100644
index 00000000..926731f7
--- /dev/null
+++ b/site/src/content/docs/ports/lua/guides/attaching-to-tmux.md
@@ -0,0 +1,122 @@
+---
+port: lua
+route: guides/attaching-to-tmux
+title: Attaching to tmux
+description: Connect to an existing tmux server and find a session with Lua.
+sidebar:
+ label: Attaching to tmux
+ group: Guides
+ order: 3
+tableOfContents: true
+---
+
+Connect a `Server` to an explicit socket and find the existing `work` session.
+The program prints its name and leaves the tmux server running. It reports an
+error if the connection fails or the session is absent.
+
+This controls tmux from your program. To open a session in your terminal, use
+`tmux attach-session`; the [shared guide](../../../../guides/attaching-to-tmux/) covers
+interactive attachment and detaching.
+
+
+
+## Connect to an existing server
+
+Save the complete program as `connect.lua`. `LIBTMUX_SOCKET_PATH` selects
+the existing server. The launcher below supplies a private socket for trying
+the example.
+
+```lua title="connect.lua"
+local adapter = require("libtmux.runtime.luv")
+
+local function must(value, err)
+ if err ~= nil then error(tostring(err), 0) end
+ return value
+end
+
+local socket = assert(os.getenv("LIBTMUX_SOCKET_PATH"), "set LIBTMUX_SOCKET_PATH")
+local binary = assert(os.getenv("TMUX_BIN"), "set TMUX_BIN to the absolute tmux path")
+
+must(adapter.run(function(runtime)
+ local server = must(runtime:connect({ binary = binary, socket_path = socket }):await())
+ local snapshot, capture_error = server:snapshot({ strict = true }):await()
+ local closed, close_error = server:close():await()
+ must(snapshot, capture_error)
+ must(closed, close_error)
+ for _, session in ipairs(snapshot.sessions) do
+ if session.name == "work" then
+ print("work")
+ return true
+ end
+ end
+ error("The work session does not exist", 0)
+end))
+```
+
+## Setup and run
+
+Use an empty directory on Linux with Git and tmux 3.2a or newer installed.
+
+This example was checked with Lua 5.5.1, luv 1.52.1-0.
+
+Save the launcher as `run.sh`. It starts an isolated tmux server, runs the
+program, checks that the session still exists, then stops only that server.
+Cleanup runs after failures too. A failed shutdown keeps its socket directory
+and prints its location for inspection.
+
+```sh title="run.sh"
+#!/bin/sh
+set -eu
+
+binary=$(command -v tmux)
+directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-lua-attach.XXXXXX")
+socket="$directory/tmux.sock"
+
+cleanup() {
+ status=$?
+ trap - 0 HUP INT TERM
+ if [ -S "$socket" ] && ! "$binary" -S "$socket" kill-server; then
+ printf 'Cannot stop tmux; kept %s\n' "$directory" >&2
+ exit 1
+ fi
+ rm -rf "$directory" || exit 1
+ exit "$status"
+}
+trap cleanup 0
+trap 'exit 1' HUP INT TERM
+
+unset TMUX TMUX_PANE
+export LIBTMUX_SOCKET_PATH="$socket" TMUX_BIN="$binary"
+"$binary" -S "$socket" -f /dev/null new-session -d -s work /bin/cat
+"$@"
+"$binary" -S "$socket" has-session -t '=work'
+```
+
+Fetch the verified library revision, build, and run:
+
+```console
+$ git clone https://github.com/libtmux/libtmux-lua libtmux-source &&
+ git -C libtmux-source checkout 5baa3f9b830ebdbc76fb50b5b3d7a5ad3f76d443 &&
+ luarocks --tree ./rocks install luv 1.52.1-0 &&
+ (cd libtmux-source &&
+ luarocks --tree ../rocks make rockspecs/libtmux-scm-1.rockspec) &&
+ eval "$(luarocks --tree ./rocks path)" &&
+ sh run.sh lua connect.lua
+```
+
+The program prints `work`. To use an existing server of your own, set
+`LIBTMUX_SOCKET_PATH` to its socket and run the program without the launcher.
+Also set `TMUX_BIN` to the absolute path of the tmux executable.
+The launcher is responsible for the demonstration server's lifetime.
+
+
+
+## Find or create a session
+
+The example only looks up a session. If your application creates a session
+after an unsuccessful lookup, another client may create the same name between
+those operations. Handle the creation error instead of assuming the lookup
+reserves the name.
+
+For a complete program that starts and owns its server, see
+[Capture pane output](/examples/capture-pane-output/).
diff --git a/site/src/content/docs/ports/py/guides/attaching-to-tmux.md b/site/src/content/docs/ports/py/guides/attaching-to-tmux.md
new file mode 100644
index 00000000..62312f08
--- /dev/null
+++ b/site/src/content/docs/ports/py/guides/attaching-to-tmux.md
@@ -0,0 +1,101 @@
+---
+port: py
+route: guides/attaching-to-tmux
+title: Attaching to tmux
+description: Connect to an existing tmux server and find a session with Python.
+sidebar:
+ label: Attaching to tmux
+ group: Guides
+ order: 3
+tableOfContents: true
+---
+
+Connect a `Server` to an explicit socket and find the existing `work` session.
+The program prints its name and leaves the tmux server running. It reports an
+error if the connection fails or the session is absent.
+
+This controls tmux from your program. To open a session in your terminal, use
+`tmux attach-session`; the [shared guide](../../../../guides/attaching-to-tmux/) covers
+interactive attachment and detaching.
+
+
+
+## Connect to an existing server
+
+Save the complete program as `connect.py`. `LIBTMUX_SOCKET_PATH` selects
+the existing server. The launcher below supplies a private socket for trying
+the example.
+
+```python title="connect.py"
+import os
+
+import libtmux
+
+server = libtmux.Server(socket_path=os.environ["LIBTMUX_SOCKET_PATH"])
+session = server.sessions.get(session_name="work")
+print(session.session_name)
+```
+
+## Setup and run
+
+Use an empty directory on Linux with Git and tmux 3.2a or newer installed.
+
+This example was checked with Python 3.14.6.
+
+Save the launcher as `run.sh`. It starts an isolated tmux server, runs the
+program, checks that the session still exists, then stops only that server.
+Cleanup runs after failures too. A failed shutdown keeps its socket directory
+and prints its location for inspection.
+
+```sh title="run.sh"
+#!/bin/sh
+set -eu
+
+binary=$(command -v tmux)
+directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-py-attach.XXXXXX")
+socket="$directory/tmux.sock"
+
+cleanup() {
+ status=$?
+ trap - 0 HUP INT TERM
+ if [ -S "$socket" ] && ! "$binary" -S "$socket" kill-server; then
+ printf 'Cannot stop tmux; kept %s\n' "$directory" >&2
+ exit 1
+ fi
+ rm -rf "$directory" || exit 1
+ exit "$status"
+}
+trap cleanup 0
+trap 'exit 1' HUP INT TERM
+
+unset TMUX TMUX_PANE
+export LIBTMUX_SOCKET_PATH="$socket" TMUX_BIN="$binary"
+"$binary" -S "$socket" -f /dev/null new-session -d -s work /bin/cat
+"$@"
+"$binary" -S "$socket" has-session -t '=work'
+```
+
+Fetch the verified library revision, build, and run:
+
+```console
+$ sh run.sh uv run \
+ --with 'libtmux @ git+https://github.com/tmux-python/libtmux@9fdd083a8181a827889337b63cd4e6daea661a8c' \
+ connect.py
+```
+
+The program prints `work`. To use an existing server of your own, set
+`LIBTMUX_SOCKET_PATH` to its socket and run the program without the launcher.
+That launcher is responsible for the demonstration server's lifetime.
+
+
+
+## Find or create a session
+
+The example only looks up a session. If your application creates a session
+after an unsuccessful lookup, another client may create the same name between
+those operations. Handle the creation error instead of assuming the lookup
+reserves the name.
+
+For a complete program that starts and owns its server, see
+[Capture pane output](/examples/capture-pane-output/). Continue with
+[Sending keys](/guides/sending-keys/) once you have a pane handle.
diff --git a/site/src/content/docs/ports/rs/guides/attaching-to-tmux.md b/site/src/content/docs/ports/rs/guides/attaching-to-tmux.md
new file mode 100644
index 00000000..31818f7d
--- /dev/null
+++ b/site/src/content/docs/ports/rs/guides/attaching-to-tmux.md
@@ -0,0 +1,134 @@
+---
+port: rs
+route: guides/attaching-to-tmux
+title: Attaching to tmux
+description: Connect to an existing tmux server and find a session with Rust.
+sidebar:
+ label: Attaching to tmux
+ group: Guides
+ order: 3
+tableOfContents: true
+---
+
+Connect a `Server` to an explicit socket and find the existing `work` session.
+The program prints its name and leaves the tmux server running. It reports an
+error if the connection fails or the session is absent.
+
+This controls tmux from your program. To open a session in your terminal, use
+`tmux attach-session`; the [shared guide](../../../../guides/attaching-to-tmux/) covers
+interactive attachment and detaching.
+
+
+
+## Connect to an existing server
+
+Save the complete program as `src/main.rs`. `LIBTMUX_SOCKET_PATH` selects
+the existing server. The launcher below supplies a private socket for trying
+the example.
+
+```rust title="src/main.rs"
+use std::error::Error;
+use std::time::Duration;
+
+use libtmux::Server;
+use tokio::time::timeout;
+
+#[tokio::main]
+async fn main() -> Result<(), Box> {
+ let socket = std::env::var("LIBTMUX_SOCKET_PATH")?;
+ let server = Server::builder().socket_path(socket).build()?;
+ let result = timeout(Duration::from_secs(5), async {
+ let sessions = server.sessions().await?;
+ if !sessions
+ .iter()
+ .any(|session| session.name().as_bytes() == b"work")
+ {
+ return Err("The work session does not exist".into());
+ }
+ println!("work");
+ Ok::<_, Box>(())
+ })
+ .await;
+ let closed = server.shutdown().await;
+ result??;
+ closed?;
+ Ok(())
+}
+```
+
+## Setup and run
+
+Use an empty directory on Linux with Git and tmux 3.2a or newer installed.
+
+This example was checked with Rust 1.97.1.
+
+Save this file beside the program using the displayed filename.
+
+```toml title="Cargo.toml"
+[package]
+name = "connect-example"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+libtmux = { path = "libtmux/crates/libtmux" }
+tokio = { version = "1.53.1", features = ["macros", "rt-multi-thread", "time"] }
+```
+
+Save the launcher as `run.sh`. It starts an isolated tmux server, runs the
+program, checks that the session still exists, then stops only that server.
+Cleanup runs after failures too. A failed shutdown keeps its socket directory
+and prints its location for inspection.
+
+```sh title="run.sh"
+#!/bin/sh
+set -eu
+
+binary=$(command -v tmux)
+directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-rs-attach.XXXXXX")
+socket="$directory/tmux.sock"
+
+cleanup() {
+ status=$?
+ trap - 0 HUP INT TERM
+ if [ -S "$socket" ] && ! "$binary" -S "$socket" kill-server; then
+ printf 'Cannot stop tmux; kept %s\n' "$directory" >&2
+ exit 1
+ fi
+ rm -rf "$directory" || exit 1
+ exit "$status"
+}
+trap cleanup 0
+trap 'exit 1' HUP INT TERM
+
+unset TMUX TMUX_PANE
+export LIBTMUX_SOCKET_PATH="$socket" TMUX_BIN="$binary"
+"$binary" -S "$socket" -f /dev/null new-session -d -s work /bin/cat
+"$@"
+"$binary" -S "$socket" has-session -t '=work'
+```
+
+Fetch the verified library revision, build, and run:
+
+```console
+$ git clone https://github.com/libtmux/libtmux-rs libtmux &&
+ git -C libtmux checkout d4e08b4eaab62ef4eeedab79b47973ae9a1de310 &&
+ sh run.sh cargo +1.97.1 run
+```
+
+The program prints `work`. To use an existing server of your own, set
+`LIBTMUX_SOCKET_PATH` to its socket and run the program without the launcher.
+That launcher is responsible for the demonstration server's lifetime.
+
+
+
+## Find or create a session
+
+The example only looks up a session. If your application creates a session
+after an unsuccessful lookup, another client may create the same name between
+those operations. Handle the creation error instead of assuming the lookup
+reserves the name.
+
+For a complete program that starts and owns its server, see
+[Capture pane output](/examples/capture-pane-output/). Continue with
+[Sending keys](/guides/sending-keys/) once you have a pane handle.
diff --git a/site/src/content/docs/ports/rs/workspace/internals/examples.md b/site/src/content/docs/ports/rs/workspace/internals/examples.md
index 0ca1a15b..5dd901e4 100644
--- a/site/src/content/docs/ports/rs/workspace/internals/examples.md
+++ b/site/src/content/docs/ports/rs/workspace/internals/examples.md
@@ -36,6 +36,10 @@ Create the project manifest. Both library crates use that source tree. The
`test-support` feature provides the public `TestServer` helper, which creates
and owns the isolated server used by this example.
+`TestServer` is temporary example scaffolding. In an application, use a
+[`Server`](../../../reference/server-server/) connected to the tmux server you manage.
+Future versions of this example will use that regular server object directly.
+
```toml title="Cargo.toml"
[package]
name = "workspace-example"
diff --git a/site/src/content/docs/ports/ruby/guides/attaching-to-tmux.md b/site/src/content/docs/ports/ruby/guides/attaching-to-tmux.md
new file mode 100644
index 00000000..3859d042
--- /dev/null
+++ b/site/src/content/docs/ports/ruby/guides/attaching-to-tmux.md
@@ -0,0 +1,110 @@
+---
+port: ruby
+route: guides/attaching-to-tmux
+title: Attaching to tmux
+description: Connect to an existing tmux server and find a session with Ruby.
+sidebar:
+ label: Attaching to tmux
+ group: Guides
+ order: 3
+tableOfContents: true
+---
+
+Connect a `Server` to an explicit socket and find the existing `work` session.
+The program prints its name and leaves the tmux server running. It reports an
+error if the connection fails or the session is absent.
+
+This controls tmux from your program. To open a session in your terminal, use
+`tmux attach-session`; the [shared guide](../../../../guides/attaching-to-tmux/) covers
+interactive attachment and detaching.
+
+
+
+## Connect to an existing server
+
+Save the complete program as `connect.rb`. `LIBTMUX_SOCKET_PATH` selects
+the existing server. The launcher below supplies a private socket for trying
+the example.
+
+```ruby title="connect.rb"
+require "libtmux"
+
+LibTmux::Server.open(socket_path: ENV.fetch("LIBTMUX_SOCKET_PATH")) do |server|
+ session = server.snapshot.sessions.find { |candidate| candidate.name == "work" }
+ raise "The work session does not exist" unless session
+ puts session.name
+end
+```
+
+## Setup and run
+
+Use an empty directory on Linux with Git and tmux 3.2a or newer installed.
+
+This example was checked with Ruby 4.0.7, Bundler.
+
+Save this file beside the program using the displayed filename.
+
+```ruby title="Gemfile"
+source "https://rubygems.org"
+
+gem "libtmux", path: "libtmux-source/gems/libtmux"
+```
+
+Save the launcher as `run.sh`. It starts an isolated tmux server, runs the
+program, checks that the session still exists, then stops only that server.
+Cleanup runs after failures too. A failed shutdown keeps its socket directory
+and prints its location for inspection.
+
+```sh title="run.sh"
+#!/bin/sh
+set -eu
+
+binary=$(command -v tmux)
+directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-ruby-attach.XXXXXX")
+socket="$directory/tmux.sock"
+
+cleanup() {
+ status=$?
+ trap - 0 HUP INT TERM
+ if [ -S "$socket" ] && ! "$binary" -S "$socket" kill-server; then
+ printf 'Cannot stop tmux; kept %s\n' "$directory" >&2
+ exit 1
+ fi
+ rm -rf "$directory" || exit 1
+ exit "$status"
+}
+trap cleanup 0
+trap 'exit 1' HUP INT TERM
+
+unset TMUX TMUX_PANE
+export LIBTMUX_SOCKET_PATH="$socket" TMUX_BIN="$binary"
+"$binary" -S "$socket" -f /dev/null new-session -d -s work /bin/cat
+"$@"
+"$binary" -S "$socket" has-session -t '=work'
+```
+
+Fetch the verified library revision, build, and run:
+
+```console
+$ git clone https://github.com/libtmux/libtmux-ruby libtmux-source &&
+ git -C libtmux-source checkout 2599d45369515aaf2fd5793bfe71cdc20de641b6 &&
+ bundle config set --local path vendor/bundle &&
+ bundle install &&
+ sh run.sh bundle exec ruby connect.rb
+```
+
+The program prints `work`. To use an existing server of your own, set
+`LIBTMUX_SOCKET_PATH` to its socket and run the program without the launcher.
+That launcher is responsible for the demonstration server's lifetime.
+
+
+
+## Find or create a session
+
+The example only looks up a session. If your application creates a session
+after an unsuccessful lookup, another client may create the same name between
+those operations. Handle the creation error instead of assuming the lookup
+reserves the name.
+
+For a complete program that starts and owns its server, see
+[Capture pane output](/examples/capture-pane-output/).
diff --git a/site/src/content/docs/ports/scala/guides/attaching-to-tmux.md b/site/src/content/docs/ports/scala/guides/attaching-to-tmux.md
new file mode 100644
index 00000000..9a1db09e
--- /dev/null
+++ b/site/src/content/docs/ports/scala/guides/attaching-to-tmux.md
@@ -0,0 +1,152 @@
+---
+port: scala
+route: guides/attaching-to-tmux
+title: Attaching to tmux
+description: Connect to an existing tmux server and find a session with Scala.
+sidebar:
+ label: Attaching to tmux
+ group: Guides
+ order: 3
+tableOfContents: true
+---
+
+Connect a `Server` to an explicit socket and find the existing `work` session.
+The program prints its name and leaves the tmux server running. It reports an
+error if the connection fails or the session is absent.
+
+This controls tmux from your program. To open a session in your terminal, use
+`tmux attach-session`; the [shared guide](../../../../guides/attaching-to-tmux/) covers
+interactive attachment and detaching.
+
+
+
+## Connect to an existing server
+
+Save the complete program as `Connect.scala`. `LIBTMUX_SOCKET_PATH` selects
+the existing server. The launcher below supplies a private socket for trying
+the example.
+
+```scala title="Connect.scala"
+import io.github.libtmux.{ServerConfig, ServerEndpoint}
+import io.github.libtmux.scaladsl.*
+import java.nio.file.Path
+import java.time.Duration
+import scala.util.Using
+
+object Connect {
+ def main(args: Array[String]): Unit = {
+ val socket = sys.env.getOrElse("LIBTMUX_SOCKET_PATH",
+ throw new IllegalArgumentException("Set LIBTMUX_SOCKET_PATH to an existing socket"))
+ val config = ServerConfig.builder()
+ .endpoint(ServerEndpoint.socketPath(Path.of(socket)))
+ .defaultTimeout(Duration.ofSeconds(5))
+ .build()
+ Using.resource(Server.open(config)) { server =>
+ val session = server.sessions().find(_.name == "work").getOrElse(
+ throw new IllegalStateException("The work session does not exist"))
+ println(session.name)
+ }
+ }
+}
+```
+
+## Setup and run
+
+Use an empty directory on Linux with Git and tmux 3.2a or newer installed.
+
+This example was checked with JDK 25.0.3, Scala 3.9.0.
+
+Save this file beside the program using the displayed filename.
+
+```kotlin title="build.gradle.kts"
+plugins {
+ application
+ scala
+}
+
+repositories { mavenCentral() }
+dependencies {
+ implementation("org.scala-lang:scala3-library_3:3.9.0")
+ implementation("io.github.libtmux:libtmux-scala_3:0.0.1-alpha.17-SNAPSHOT")
+}
+java { toolchain { languageVersion.set(JavaLanguageVersion.of(25)) } }
+sourceSets.main { scala.srcDir("."); scala.include("Connect.scala") }
+application { mainClass.set("Connect") }
+```
+
+Save this file beside the program using the displayed filename.
+
+```kotlin title="settings.gradle.kts"
+rootProject.name = "connect"
+includeBuild("libtmux-source") {
+ dependencySubstitution {
+ substitute(module("io.github.libtmux:libtmux-scala_3"))
+ .using(project(":libtmux-scala"))
+ }
+}
+```
+
+Save this file beside the program using the displayed filename.
+
+```properties title="gradle.properties"
+org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8
+kotlin.daemon.jvmargs=-Xmx2g
+org.gradle.workers.max=2
+```
+
+Save the launcher as `run.sh`. It starts an isolated tmux server, runs the
+program, checks that the session still exists, then stops only that server.
+Cleanup runs after failures too. A failed shutdown keeps its socket directory
+and prints its location for inspection.
+
+```sh title="run.sh"
+#!/bin/sh
+set -eu
+
+binary=$(command -v tmux)
+directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-scala-attach.XXXXXX")
+socket="$directory/tmux.sock"
+
+cleanup() {
+ status=$?
+ trap - 0 HUP INT TERM
+ if [ -S "$socket" ] && ! "$binary" -S "$socket" kill-server; then
+ printf 'Cannot stop tmux; kept %s\n' "$directory" >&2
+ exit 1
+ fi
+ rm -rf "$directory" || exit 1
+ exit "$status"
+}
+trap cleanup 0
+trap 'exit 1' HUP INT TERM
+
+unset TMUX TMUX_PANE
+export LIBTMUX_SOCKET_PATH="$socket" TMUX_BIN="$binary"
+"$binary" -S "$socket" -f /dev/null new-session -d -s work /bin/cat
+"$@"
+"$binary" -S "$socket" has-session -t '=work'
+```
+
+Fetch the verified library revision, build, and run:
+
+```console
+$ git clone https://github.com/libtmux/libtmux-java libtmux-source &&
+ git -C libtmux-source checkout 85ebf6955e56703c5be74e2afd34a18309044741 &&
+ sh run.sh ./libtmux-source/gradlew --project-dir . run --console=plain --max-workers=2
+```
+
+The program prints `work`. To use an existing server of your own, set
+`LIBTMUX_SOCKET_PATH` to its socket and run the program without the launcher.
+That launcher is responsible for the demonstration server's lifetime.
+
+
+
+## Find or create a session
+
+The example only looks up a session. If your application creates a session
+after an unsuccessful lookup, another client may create the same name between
+those operations. Handle the creation error instead of assuming the lookup
+reserves the name.
+
+For a complete program that starts and owns its server, see
+[Capture pane output](/examples/capture-pane-output/).
diff --git a/site/src/content/docs/ports/swift/guides/attaching-to-tmux.md b/site/src/content/docs/ports/swift/guides/attaching-to-tmux.md
new file mode 100644
index 00000000..4f7c6abb
--- /dev/null
+++ b/site/src/content/docs/ports/swift/guides/attaching-to-tmux.md
@@ -0,0 +1,133 @@
+---
+port: swift
+route: guides/attaching-to-tmux
+title: Attaching to tmux
+description: Connect to an existing tmux server and find a session with Swift.
+sidebar:
+ label: Attaching to tmux
+ group: Guides
+ order: 3
+tableOfContents: true
+---
+
+Connect a `Server` to an explicit socket and find the existing `work` session.
+The program prints its name and leaves the tmux server running. It reports an
+error if the connection fails or the session is absent.
+
+This controls tmux from your program. To open a session in your terminal, use
+`tmux attach-session`; the [shared guide](../../../../guides/attaching-to-tmux/) covers
+interactive attachment and detaching.
+
+
+
+## Connect to an existing server
+
+Save the complete program as `Sources/Connect/Connect.swift`. `LIBTMUX_SOCKET_PATH` selects
+the existing server. The launcher below supplies a private socket for trying
+the example.
+
+```swift title="Connect.swift"
+import Foundation
+import LibTmux
+
+enum ConnectError: Error {
+ case failed(String)
+}
+
+@main
+struct Connect {
+ static func main() async throws {
+ guard let socket = ProcessInfo.processInfo.environment["LIBTMUX_SOCKET_PATH"] else {
+ throw ConnectError.failed("Set LIBTMUX_SOCKET_PATH to an existing socket")
+ }
+ let server = try Server(socketPath: socket)
+ guard try await server.hasSession("=work") else {
+ throw ConnectError.failed("The work session does not exist")
+ }
+ print("work")
+ }
+}
+```
+
+## Setup and run
+
+Use an empty directory on Linux with Git and tmux 3.2a or newer installed.
+
+This example was checked with Swift 6.2.4.
+
+Save this file beside the program using the displayed filename.
+
+```swift title="Package.swift"
+// swift-tools-version: 6.2
+import PackageDescription
+
+let package = Package(
+ name: "ConnectExample",
+ platforms: [.macOS(.v13)],
+ dependencies: [.package(path: "libtmux-source")],
+ targets: [
+ .executableTarget(
+ name: "Connect",
+ dependencies: [.product(name: "LibTmux", package: "libtmux-source")]
+ ),
+ ]
+)
+```
+
+Save the launcher as `run.sh`. It starts an isolated tmux server, runs the
+program, checks that the session still exists, then stops only that server.
+Cleanup runs after failures too. A failed shutdown keeps its socket directory
+and prints its location for inspection.
+
+```sh title="run.sh"
+#!/bin/sh
+set -eu
+
+binary=$(command -v tmux)
+directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-swift-attach.XXXXXX")
+socket="$directory/tmux.sock"
+
+cleanup() {
+ status=$?
+ trap - 0 HUP INT TERM
+ if [ -S "$socket" ] && ! "$binary" -S "$socket" kill-server; then
+ printf 'Cannot stop tmux; kept %s\n' "$directory" >&2
+ exit 1
+ fi
+ rm -rf "$directory" || exit 1
+ exit "$status"
+}
+trap cleanup 0
+trap 'exit 1' HUP INT TERM
+
+unset TMUX TMUX_PANE
+export LIBTMUX_SOCKET_PATH="$socket" TMUX_BIN="$binary"
+"$binary" -S "$socket" -f /dev/null new-session -d -s work /bin/cat
+"$@"
+"$binary" -S "$socket" has-session -t '=work'
+```
+
+Fetch the verified library revision, build, and run:
+
+```console
+$ git clone https://github.com/libtmux/libtmux-swift libtmux-source &&
+ git -C libtmux-source checkout 254f8b2be7eb60cacc3ffcb3ea8e456784f582df &&
+ sh run.sh swift run --jobs 2 Connect
+```
+
+The program prints `work`. To use an existing server of your own, set
+`LIBTMUX_SOCKET_PATH` to its socket and run the program without the launcher.
+That launcher is responsible for the demonstration server's lifetime.
+
+
+
+## Find or create a session
+
+The example only looks up a session. If your application creates a session
+after an unsuccessful lookup, another client may create the same name between
+those operations. Handle the creation error instead of assuming the lookup
+reserves the name.
+
+For a complete program that starts and owns its server, see
+[Capture pane output](/examples/capture-pane-output/). Continue with
+[Sending keys](/guides/sending-keys/) once you have a pane handle.
diff --git a/site/src/content/docs/ports/ts/guides/attaching-to-tmux.md b/site/src/content/docs/ports/ts/guides/attaching-to-tmux.md
new file mode 100644
index 00000000..c43343b7
--- /dev/null
+++ b/site/src/content/docs/ports/ts/guides/attaching-to-tmux.md
@@ -0,0 +1,109 @@
+---
+port: ts
+route: guides/attaching-to-tmux
+title: Attaching to tmux
+description: Connect to an existing tmux server and find a session with TypeScript.
+sidebar:
+ label: Attaching to tmux
+ group: Guides
+ order: 3
+tableOfContents: true
+---
+
+Connect a `Server` to an explicit socket and find the existing `work` session.
+The program prints its name and leaves the tmux server running. It reports an
+error if the connection fails or the session is absent.
+
+This controls tmux from your program. To open a session in your terminal, use
+`tmux attach-session`; the [shared guide](../../../../guides/attaching-to-tmux/) covers
+interactive attachment and detaching.
+
+
+
+## Connect to an existing server
+
+Save the complete program as `connect.ts`. `LIBTMUX_SOCKET_PATH` selects
+the existing server. The launcher below supplies a private socket for trying
+the example.
+
+```typescript title="connect.ts"
+import { Server } from "libtmux";
+
+const socketPath = process.env.LIBTMUX_SOCKET_PATH;
+if (!socketPath) throw new Error("Set LIBTMUX_SOCKET_PATH to an existing socket");
+const server = new Server({ socketPath, timeoutMs: 5_000 });
+const snapshot = await server.snapshot({ signal: AbortSignal.timeout(5_000) });
+const session = snapshot.sessions.one({ name: "work" });
+console.log(session.name);
+```
+
+## Setup and run
+
+Use an empty directory on Linux with Git and tmux 3.2a or newer installed.
+
+This example was checked with Bun 1.4.2.
+
+Save this file beside the program using the displayed filename.
+
+```json title="package.json"
+{"type":"module","dependencies":{"libtmux":"file:./libtmux/packages/libtmux"}}
+```
+
+Save the launcher as `run.sh`. It starts an isolated tmux server, runs the
+program, checks that the session still exists, then stops only that server.
+Cleanup runs after failures too. A failed shutdown keeps its socket directory
+and prints its location for inspection.
+
+```sh title="run.sh"
+#!/bin/sh
+set -eu
+
+binary=$(command -v tmux)
+directory=$(mktemp -d "${TMPDIR:-/tmp}/libtmux-ts-attach.XXXXXX")
+socket="$directory/tmux.sock"
+
+cleanup() {
+ status=$?
+ trap - 0 HUP INT TERM
+ if [ -S "$socket" ] && ! "$binary" -S "$socket" kill-server; then
+ printf 'Cannot stop tmux; kept %s\n' "$directory" >&2
+ exit 1
+ fi
+ rm -rf "$directory" || exit 1
+ exit "$status"
+}
+trap cleanup 0
+trap 'exit 1' HUP INT TERM
+
+unset TMUX TMUX_PANE
+export LIBTMUX_SOCKET_PATH="$socket" TMUX_BIN="$binary"
+"$binary" -S "$socket" -f /dev/null new-session -d -s work /bin/cat
+"$@"
+"$binary" -S "$socket" has-session -t '=work'
+```
+
+Fetch the verified library revision, build, and run:
+
+```console
+$ git clone https://github.com/libtmux/libtmux-ts libtmux &&
+ git -C libtmux checkout 3fe1ca654b81b8cbf4a13b777a001a3298c87a6f &&
+ bun install &&
+ sh run.sh bun run connect.ts
+```
+
+The program prints `work`. To use an existing server of your own, set
+`LIBTMUX_SOCKET_PATH` to its socket and run the program without the launcher.
+That launcher is responsible for the demonstration server's lifetime.
+
+
+
+## Find or create a session
+
+The example only looks up a session. If your application creates a session
+after an unsuccessful lookup, another client may create the same name between
+those operations. Handle the creation error instead of assuming the lookup
+reserves the name.
+
+For a complete program that starts and owns its server, see
+[Capture pane output](/examples/capture-pane-output/). Continue with
+[Sending keys](/guides/sending-keys/) once you have a pane handle.
diff --git a/site/src/data/mentions.json b/site/src/data/mentions.json
index 09710aea..e366831a 100644
--- a/site/src/data/mentions.json
+++ b/site/src/data/mentions.json
@@ -259,6 +259,20 @@
"title": "Traversal",
"section": "topics"
},
+ {
+ "port": "cxx",
+ "symbol": "libtmux::Server",
+ "page": "/cxx/latest/examples/capture-pane-output/",
+ "title": "Capture pane output",
+ "section": "examples"
+ },
+ {
+ "port": "cxx",
+ "symbol": "libtmux::Server",
+ "page": "/cxx/latest/guides/attaching-to-tmux/",
+ "title": "Attaching to tmux",
+ "section": "guides"
+ },
{
"port": "cxx",
"symbol": "libtmux::Server",
@@ -273,6 +287,13 @@
"title": "C++ workspace internals",
"section": "ports"
},
+ {
+ "port": "cxx",
+ "symbol": "libtmux::Server",
+ "page": "/cxx/latest/workspace/internals/examples/",
+ "title": "C++ workspace builder examples",
+ "section": "ports"
+ },
{
"port": "cxx",
"symbol": "libtmux::Server",
@@ -434,6 +455,20 @@
"title": "Filtering and queries",
"section": "concepts"
},
+ {
+ "port": "cxx",
+ "symbol": "libtmux::test::ScopedTmuxServer",
+ "page": "/cxx/latest/examples/capture-pane-output/",
+ "title": "Capture pane output",
+ "section": "examples"
+ },
+ {
+ "port": "cxx",
+ "symbol": "libtmux::test::ScopedTmuxServer",
+ "page": "/cxx/latest/workspace/internals/examples/",
+ "title": "C++ workspace builder examples",
+ "section": "ports"
+ },
{
"port": "cxx",
"symbol": "libtmux::test::ScopedTmuxServer",
@@ -777,13 +812,6 @@
"title": ".NET MCP API",
"section": "ports"
},
- {
- "port": "dotnet",
- "symbol": "LibTmux.NewSessionRequest.ReplaceExisting",
- "page": "/guides/attaching-to-tmux/",
- "title": "Attaching to tmux",
- "section": "guides"
- },
{
"port": "dotnet",
"symbol": "LibTmux.OwnedSessionScope",
@@ -868,6 +896,13 @@
"title": "Pane interaction",
"section": "topics"
},
+ {
+ "port": "dotnet",
+ "symbol": "LibTmux.Server",
+ "page": "/dotnet/latest/guides/attaching-to-tmux/",
+ "title": "Attaching to tmux",
+ "section": "guides"
+ },
{
"port": "dotnet",
"symbol": "LibTmux.Server",
@@ -896,13 +931,6 @@
"title": "Use the .NET workspace builder",
"section": "ports"
},
- {
- "port": "dotnet",
- "symbol": "LibTmux.Server.CreateSessionAsync",
- "page": "/guides/attaching-to-tmux/",
- "title": "Attaching to tmux",
- "section": "guides"
- },
{
"port": "dotnet",
"symbol": "LibTmux.Server.EnterControlModeAsync",
@@ -924,13 +952,6 @@
"title": "Traversal",
"section": "topics"
},
- {
- "port": "dotnet",
- "symbol": "LibTmux.Server.HasSessionAsync",
- "page": "/guides/attaching-to-tmux/",
- "title": "Attaching to tmux",
- "section": "guides"
- },
{
"port": "dotnet",
"symbol": "LibTmux.Server.IsAliveAsync",
@@ -1680,6 +1701,13 @@
"title": "Control mode vs one-shot",
"section": "concepts"
},
+ {
+ "port": "go",
+ "symbol": "tmux.Server",
+ "page": "/go/latest/guides/attaching-to-tmux/",
+ "title": "Attaching to tmux",
+ "section": "guides"
+ },
{
"port": "go",
"symbol": "tmux.Server",
@@ -2471,6 +2499,13 @@
"title": "Testing with libtmux",
"section": "guides"
},
+ {
+ "port": "java",
+ "symbol": "io.github.libtmux.Server.Server",
+ "page": "/java/latest/guides/attaching-to-tmux/",
+ "title": "Attaching to tmux",
+ "section": "guides"
+ },
{
"port": "java",
"symbol": "io.github.libtmux.Server.Server",
@@ -4263,6 +4298,13 @@
"title": "Testing with libtmux",
"section": "guides"
},
+ {
+ "port": "py",
+ "symbol": "libtmux.server.Server",
+ "page": "/py/latest/guides/attaching-to-tmux/",
+ "title": "Attaching to tmux",
+ "section": "guides"
+ },
{
"port": "py",
"symbol": "libtmux.server.Server",
@@ -4417,13 +4459,6 @@
"title": "Traversal",
"section": "topics"
},
- {
- "port": "py",
- "symbol": "libtmux.session.Session.attach",
- "page": "/guides/attaching-to-tmux/",
- "title": "Attaching to tmux",
- "section": "guides"
- },
{
"port": "py",
"symbol": "libtmux.session.Session.from_env",
@@ -5180,6 +5215,13 @@
"title": "Waiting and retrying",
"section": "topics"
},
+ {
+ "port": "rs",
+ "symbol": "server.Server",
+ "page": "/rs/latest/guides/attaching-to-tmux/",
+ "title": "Attaching to tmux",
+ "section": "guides"
+ },
{
"port": "rs",
"symbol": "server.Server",
@@ -5201,6 +5243,13 @@
"title": "Rust workspace internals",
"section": "ports"
},
+ {
+ "port": "rs",
+ "symbol": "server.Server",
+ "page": "/rs/latest/workspace/internals/examples/",
+ "title": "Rust workspace builder examples",
+ "section": "ports"
+ },
{
"port": "rs",
"symbol": "server.Server",
@@ -6132,6 +6181,13 @@
"title": "Swift workspace builder API",
"section": "ports"
},
+ {
+ "port": "swift",
+ "symbol": "Server",
+ "page": "/swift/latest/guides/attaching-to-tmux/",
+ "title": "Attaching to tmux",
+ "section": "guides"
+ },
{
"port": "swift",
"symbol": "Server",
@@ -7063,6 +7119,13 @@
"title": "Testing with libtmux",
"section": "guides"
},
+ {
+ "port": "ts",
+ "symbol": "server.Server",
+ "page": "/ts/latest/guides/attaching-to-tmux/",
+ "title": "Attaching to tmux",
+ "section": "guides"
+ },
{
"port": "ts",
"symbol": "server.Server",
diff --git a/site/test/complete-examples.test.ts b/site/test/complete-examples.test.ts
index 26db49eb..8b4fe985 100644
--- a/site/test/complete-examples.test.ts
+++ b/site/test/complete-examples.test.ts
@@ -4,6 +4,7 @@ import { readFileSync } from 'node:fs'
import { Window } from 'happy-dom'
import { afterEach, describe, expect, it, vi } from 'vitest'
import receipt from './fixtures/capture-examples.json'
+import attach from './fixtures/attach-examples.json'
import products from './fixtures/product-examples.json'
import { remarkPortCode, resolvePortCode } from '../src/plugins/remark-port-code.mjs'
import { rehypeCodeTabs } from '../src/plugins/rehype-code-tabs.mjs'
@@ -16,7 +17,7 @@ const bodyOf = (page: string) => parsePage(page).content
const fences = (markdown: string) => [...markdown.matchAll(/^```(\S+)([^\n]*)\n([\s\S]*?)^```/gm)]
.map((match) => ({ language: match[1], title: /title="([^"]+)"/.exec(match[2])?.[1], code: match[3] }))
const sha256 = (code: string) => createHash('sha256').update(code).digest('hex')
-const examples = [...receipt.examples, ...products.examples]
+const examples = [...receipt.examples, ...attach.examples, ...products.examples]
afterEach(() => vi.unstubAllEnvs())
@@ -37,10 +38,12 @@ describe('verified complete programs', () => {
const commands = [...selected.matchAll(/^```console\n([\s\S]*?)^```/gm)]
.map((match) => match[1].replace(/^\$ /gm, '').trim())
expect(commands).toEqual(example.shellRecipe)
- const program = blocks.find((block) => block.title === example.files[0].name)!
- const comment = ['python', 'sh'].includes(example.language) ? /^\s*#/ : /^\s*(?:\/\/|\/\*|\* )/
- for (const line of program.code.split('\n').filter((line) => comment.test(line))) {
- expect(line.length, `${example.port}: ${line}`).toBeLessThanOrEqual(80)
+ for (const block of blocks) {
+ const comment = ['python', 'sh', 'ruby', 'toml', 'cmake', 'yaml', 'properties'].includes(block.language)
+ ? /^\s*#/ : block.language === 'lua' ? /^\s*--/ : /^\s*(?:\/\/|\/\*|\* )/
+ for (const line of block.code.split('\n').filter((line) => comment.test(line))) {
+ expect(line.replaceAll('\t', ' ').length, `${example.port}: ${line}`).toBeLessThanOrEqual(100)
+ }
}
})
@@ -71,7 +74,7 @@ describe('verified complete programs', () => {
}
})
- it('keeps the root about tmux and routes each complete program to its port', async () => {
+ it.each([receipt, attach])('keeps $page about tmux and routes each program to its port', async (receipt) => {
const root = readPage(receipt.page)
expect(root).toMatch(/^supportedPorts: \[\]$/m)
const blocks = fences(bodyOf(receipt.page))
diff --git a/site/test/fixtures/attach-examples.json b/site/test/fixtures/attach-examples.json
new file mode 100644
index 00000000..73b331c3
--- /dev/null
+++ b/site/test/fixtures/attach-examples.json
@@ -0,0 +1,449 @@
+{
+ "page": "guides/attaching-to-tmux",
+ "scope": "Complete existing-server consumers with their displayed setup and isolated shell launcher.",
+ "examples": [
+ {
+ "port": "py",
+ "language": "python",
+ "sourceRevision": "9fdd083a8181a827889337b63cd4e6daea661a8c",
+ "sourceRepository": "tmux-python/libtmux",
+ "toolchain": [
+ "Python 3.14.6"
+ ],
+ "page": "ports/py/guides/attaching-to-tmux",
+ "shellRecipe": [
+ "sh run.sh uv run \\\n --with 'libtmux @ git+https://github.com/tmux-python/libtmux@9fdd083a8181a827889337b63cd4e6daea661a8c' \\\n connect.py"
+ ],
+ "expectedOutput": "work",
+ "files": [
+ {
+ "name": "connect.py",
+ "sha256": "85c37335c74572af5606ad44a165fe097ad3b2737a061a75a86a9eba6f91af52"
+ },
+ {
+ "name": "run.sh",
+ "sha256": "947db2704b348ea51541e4cffe5ac613fb0b2d5703affe8abd0f35fc8286085e"
+ }
+ ],
+ "tmux": [
+ "3.2a",
+ "3.7c"
+ ]
+ },
+ {
+ "port": "ts",
+ "language": "typescript",
+ "sourceRevision": "3fe1ca654b81b8cbf4a13b777a001a3298c87a6f",
+ "sourceRepository": "libtmux/libtmux-ts",
+ "toolchain": [
+ "Bun 1.4.2"
+ ],
+ "page": "ports/ts/guides/attaching-to-tmux",
+ "shellRecipe": [
+ "git clone https://github.com/libtmux/libtmux-ts libtmux &&\n git -C libtmux checkout 3fe1ca654b81b8cbf4a13b777a001a3298c87a6f &&\n bun install &&\n sh run.sh bun run connect.ts"
+ ],
+ "expectedOutput": "work",
+ "files": [
+ {
+ "name": "connect.ts",
+ "sha256": "bd4f946b8fc2a24e1aab9d366402fa1260a3a05542023f3454e0686c72ba3d9d"
+ },
+ {
+ "name": "package.json",
+ "sha256": "acb06879bf3e3e5ce85125d64a72ff5b2579e87a66085961b1f7b88eaa813020"
+ },
+ {
+ "name": "run.sh",
+ "sha256": "124a197acc79bf28595ebf8ea43cc9b89565d2f97ca5cc5ae64257674bdad539"
+ }
+ ],
+ "tmux": [
+ "3.2a",
+ "3.7c"
+ ]
+ },
+ {
+ "port": "go",
+ "language": "go",
+ "sourceRevision": "bb06e26e116e941813ca40bf45e7e3a47d38f52a",
+ "sourceRepository": "libtmux/libtmux-go",
+ "toolchain": [
+ "Go 1.26.8"
+ ],
+ "page": "ports/go/guides/attaching-to-tmux",
+ "shellRecipe": [
+ "git clone https://github.com/libtmux/libtmux-go libtmux &&\n git -C libtmux checkout bb06e26e116e941813ca40bf45e7e3a47d38f52a &&\n GOWORK=off go mod tidy &&\n sh run.sh env GOWORK=off go run ."
+ ],
+ "expectedOutput": "work",
+ "files": [
+ {
+ "name": "main.go",
+ "sha256": "c94705da738b099fc78dd646ee8c49e1405fea1da2259bdc327a4849684b51cc"
+ },
+ {
+ "name": "go.mod",
+ "sha256": "d8975aa3076f5cf4a15169fb558fd49404644e1cb5127299536a99c7253de4e4"
+ },
+ {
+ "name": "run.sh",
+ "sha256": "2163f09a3a6ba881afb8d0e9750910b652f39c762d31013f2dbc4b3868c3c565"
+ }
+ ],
+ "tmux": [
+ "3.2a",
+ "3.7c"
+ ]
+ },
+ {
+ "port": "rs",
+ "language": "rust",
+ "sourceRevision": "d4e08b4eaab62ef4eeedab79b47973ae9a1de310",
+ "sourceRepository": "libtmux/libtmux-rs",
+ "toolchain": [
+ "Rust 1.97.1"
+ ],
+ "page": "ports/rs/guides/attaching-to-tmux",
+ "shellRecipe": [
+ "git clone https://github.com/libtmux/libtmux-rs libtmux &&\n git -C libtmux checkout d4e08b4eaab62ef4eeedab79b47973ae9a1de310 &&\n sh run.sh cargo +1.97.1 run"
+ ],
+ "expectedOutput": "work",
+ "files": [
+ {
+ "name": "src/main.rs",
+ "sha256": "931e123008ee498523d89f0036ed480f4a9da0a47e0572a75f58df6034dc1ff1"
+ },
+ {
+ "name": "Cargo.toml",
+ "sha256": "b1126e5db8c76db064324255f98bc382a9d2ade716d89f19e8767baeebb170ee"
+ },
+ {
+ "name": "run.sh",
+ "sha256": "d984d9482d78cb452286b071afad4aca2d21ee271d44920b0818fabe8e6cc511"
+ }
+ ],
+ "tmux": [
+ "3.2a",
+ "3.7c"
+ ]
+ },
+ {
+ "port": "java",
+ "language": "java",
+ "sourceRevision": "842228310449e879ebcaa3f910597757c9dbffd6",
+ "sourceRepository": "libtmux/libtmux-java",
+ "toolchain": [
+ "JDK 25.0.3",
+ "library bytecode target Java 21"
+ ],
+ "page": "ports/java/guides/attaching-to-tmux",
+ "shellRecipe": [
+ "git clone https://github.com/libtmux/libtmux-java libtmux &&\n git -C libtmux checkout 842228310449e879ebcaa3f910597757c9dbffd6 &&\n (cd libtmux && ./gradlew :libtmux:jar) &&\n sh run.sh java --class-path 'libtmux/libtmux/build/libs/*' Connect.java"
+ ],
+ "expectedOutput": "work",
+ "files": [
+ {
+ "name": "Connect.java",
+ "sha256": "0dd7f4fef844f89ed6b6f424f3bce74daa43beb5280f43b12ab44cc1f4bf56a3"
+ },
+ {
+ "name": "run.sh",
+ "sha256": "c7c6c6c7798d10dd389beaa6a5843f145a1bf0497acae99e229468cf0add072d"
+ }
+ ],
+ "tmux": [
+ "3.2a",
+ "3.7c"
+ ]
+ },
+ {
+ "port": "dotnet",
+ "language": "csharp",
+ "sourceRevision": "320dc64f4b8b7815842471327a5e6b84a1499bf8",
+ "sourceRepository": "libtmux/libtmux-dotnet",
+ "toolchain": [
+ ".NET SDK 10.0.302"
+ ],
+ "page": "ports/dotnet/guides/attaching-to-tmux",
+ "shellRecipe": [
+ "git clone https://github.com/libtmux/libtmux-dotnet libtmux-source &&\n git -C libtmux-source checkout 320dc64f4b8b7815842471327a5e6b84a1499bf8 &&\n dotnet build Connect.csproj --maxcpucount:1 &&\n sh run.sh dotnet run --project Connect.csproj --no-build"
+ ],
+ "expectedOutput": "work",
+ "files": [
+ {
+ "name": "Program.cs",
+ "sha256": "0936735d8c29c384b1040d37eca077690017d23eb6c21b9b6de489dde595f072"
+ },
+ {
+ "name": "Connect.csproj",
+ "sha256": "badc813042195a1ac170ad3952437994fe7a85425b09b888f3e1e20f6fa71d15"
+ },
+ {
+ "name": "run.sh",
+ "sha256": "bb6c3ecf7e120beacaee494336835da637c8f2a97c2881a75654eb1d494fef9e"
+ }
+ ],
+ "tmux": [
+ "3.2a",
+ "3.7c"
+ ]
+ },
+ {
+ "port": "cxx",
+ "language": "cpp",
+ "sourceRevision": "393d4b0ad666f18a6581f1eb281741a75a7503f0",
+ "sourceRepository": "libtmux/libtmux-cxx",
+ "toolchain": [
+ "Clang 18",
+ "libc++ 18",
+ "CMake 3.25+"
+ ],
+ "page": "ports/cxx/guides/attaching-to-tmux",
+ "shellRecipe": [
+ "git clone https://github.com/libtmux/libtmux-cxx libtmux-source &&\n git -C libtmux-source checkout 393d4b0ad666f18a6581f1eb281741a75a7503f0 &&\n cmake -S . -B build -G Ninja \\\n -DCMAKE_CXX_COMPILER=clang++ \\\n -DCMAKE_CXX_FLAGS=-stdlib=libc++ \\\n -DCMAKE_EXE_LINKER_FLAGS=-stdlib=libc++ &&\n cmake --build build --target connect --parallel 2 &&\n sh run.sh ./build/connect"
+ ],
+ "expectedOutput": "work",
+ "files": [
+ {
+ "name": "connect.cpp",
+ "sha256": "e7cf8fedee0d797982c249ed194885c23c5c8f6d29e583592105dd09a191f875"
+ },
+ {
+ "name": "CMakeLists.txt",
+ "sha256": "b21ea46308497a8fb5b6c7acda8c915a568da7f1573ae08aed67b379a217713a"
+ },
+ {
+ "name": "run.sh",
+ "sha256": "e2d5c1be7459ab5be9f163902b6be34abfd8195c2d5ac003cb3342d178a829a4"
+ }
+ ],
+ "tmux": [
+ "3.2a",
+ "3.7c"
+ ]
+ },
+ {
+ "port": "swift",
+ "language": "swift",
+ "sourceRevision": "254f8b2be7eb60cacc3ffcb3ea8e456784f582df",
+ "sourceRepository": "libtmux/libtmux-swift",
+ "toolchain": [
+ "Swift 6.2.4"
+ ],
+ "page": "ports/swift/guides/attaching-to-tmux",
+ "shellRecipe": [
+ "git clone https://github.com/libtmux/libtmux-swift libtmux-source &&\n git -C libtmux-source checkout 254f8b2be7eb60cacc3ffcb3ea8e456784f582df &&\n sh run.sh swift run --jobs 2 Connect"
+ ],
+ "expectedOutput": "work",
+ "files": [
+ {
+ "name": "Connect.swift",
+ "path": "Sources/Connect/Connect.swift",
+ "sha256": "9821b15aa0bb09ab626164f6f6a1e900f7263fafc5ff3d6297520505c8ebdc07"
+ },
+ {
+ "name": "Package.swift",
+ "sha256": "bc9db4a88cdd0236c4985176f03a46c9dfac4f2401312149876d0fc013a91440"
+ },
+ {
+ "name": "run.sh",
+ "sha256": "71f88925278d500b86c6571b92063ecf723275e8983468f6429d9fe2f6b2fe56"
+ }
+ ],
+ "tmux": [
+ "3.2a",
+ "3.7c"
+ ]
+ },
+ {
+ "port": "kotlin",
+ "language": "kotlin",
+ "sourceRevision": "85ebf6955e56703c5be74e2afd34a18309044741",
+ "sourceRepository": "libtmux/libtmux-java",
+ "toolchain": [
+ "JDK 25.0.3",
+ "Kotlin 2.4.10"
+ ],
+ "page": "ports/kotlin/guides/attaching-to-tmux",
+ "shellRecipe": [
+ "git clone https://github.com/libtmux/libtmux-java libtmux-source &&\n git -C libtmux-source checkout 85ebf6955e56703c5be74e2afd34a18309044741 &&\n sh run.sh ./libtmux-source/gradlew --project-dir . run --console=plain --max-workers=2"
+ ],
+ "expectedOutput": "work",
+ "files": [
+ {
+ "name": "Connect.kt",
+ "sha256": "f908e010372c9a9391c6e20748bcdf296cce991b3822fe3f83f7016ec39a8671"
+ },
+ {
+ "name": "build.gradle.kts",
+ "sha256": "e1d0abe83d5ccb4948dc48e4c035b1f60ef45a04924f25f4711a81dded646d27"
+ },
+ {
+ "name": "settings.gradle.kts",
+ "sha256": "b00ae8397b15423ec37743ffe34966e3123fa2a21996d36457953dff131d60bc"
+ },
+ {
+ "name": "gradle.properties",
+ "sha256": "aa2f75584718e340a2dab7af0d04d010cd8d0b83fc6b16f96a141cd3244821c1"
+ },
+ {
+ "name": "run.sh",
+ "sha256": "a6a762cba4ee9405dbd633dba800fcd0824c82587daed9bb922b2b400d282f2c"
+ }
+ ],
+ "tmux": [
+ "3.2a",
+ "3.7c"
+ ]
+ },
+ {
+ "port": "scala",
+ "language": "scala",
+ "sourceRevision": "85ebf6955e56703c5be74e2afd34a18309044741",
+ "sourceRepository": "libtmux/libtmux-java",
+ "toolchain": [
+ "JDK 25.0.3",
+ "Scala 3.9.0"
+ ],
+ "page": "ports/scala/guides/attaching-to-tmux",
+ "shellRecipe": [
+ "git clone https://github.com/libtmux/libtmux-java libtmux-source &&\n git -C libtmux-source checkout 85ebf6955e56703c5be74e2afd34a18309044741 &&\n sh run.sh ./libtmux-source/gradlew --project-dir . run --console=plain --max-workers=2"
+ ],
+ "expectedOutput": "work",
+ "files": [
+ {
+ "name": "Connect.scala",
+ "sha256": "cbca6ab2fdb96a5b4291cdc9003a17d32a098c2b5f7cff159b03bd4ab63ca1a5"
+ },
+ {
+ "name": "build.gradle.kts",
+ "sha256": "745dadde4e2fff287d2bb57ee8b2679d19921af02745857b9a9a0c62c1dc04e9"
+ },
+ {
+ "name": "settings.gradle.kts",
+ "sha256": "84f6f8c1760984676ed15761a8838fba725ee18fee25a28b8cc3f94ad3e24b99"
+ },
+ {
+ "name": "gradle.properties",
+ "sha256": "aa2f75584718e340a2dab7af0d04d010cd8d0b83fc6b16f96a141cd3244821c1"
+ },
+ {
+ "name": "run.sh",
+ "sha256": "200098b228e8bff4cf7c60ccdb8d43c8222c95c52c20e0b69b23c1bbb6c8ea8f"
+ }
+ ],
+ "tmux": [
+ "3.2a",
+ "3.7c"
+ ]
+ },
+ {
+ "port": "fsharp",
+ "language": "fsharp",
+ "sourceRevision": "661287848a6cfb407f37114b25e8249a29f99e3f",
+ "sourceRepository": "libtmux/libtmux-dotnet",
+ "toolchain": [
+ ".NET SDK 10.0.302"
+ ],
+ "page": "ports/fsharp/guides/attaching-to-tmux",
+ "shellRecipe": [
+ "git clone https://github.com/libtmux/libtmux-dotnet libtmux-source &&\n git -C libtmux-source checkout 661287848a6cfb407f37114b25e8249a29f99e3f &&\n dotnet build Connect.fsproj --maxcpucount:1 \\\n -p:DisableImplicitLibraryPacksFolder=true \\\n -p:RestorePackagesPath=\"$PWD/.packages\" &&\n sh run.sh dotnet run --project Connect.fsproj --no-build"
+ ],
+ "expectedOutput": "work",
+ "files": [
+ {
+ "name": "Program.fs",
+ "sha256": "22a62e8423aa21ecb88aba93bf1cbf0520aa84c968ce18bb0559122b314517bc"
+ },
+ {
+ "name": "Connect.fsproj",
+ "sha256": "314bfb2bef111e8885728a0065943a59594721011da4ac6ef0275aebd8f14bcd"
+ },
+ {
+ "name": "run.sh",
+ "sha256": "ecb16ad7a624faf847875e73372706fa988893af7177c46a7912ed9364de88da"
+ }
+ ],
+ "tmux": [
+ "3.2a",
+ "3.7c"
+ ]
+ },
+ {
+ "port": "ruby",
+ "language": "ruby",
+ "sourceRevision": "2599d45369515aaf2fd5793bfe71cdc20de641b6",
+ "sourceRepository": "libtmux/libtmux-ruby",
+ "toolchain": [
+ "Ruby 4.0.7",
+ "Bundler"
+ ],
+ "page": "ports/ruby/guides/attaching-to-tmux",
+ "shellRecipe": [
+ "git clone https://github.com/libtmux/libtmux-ruby libtmux-source &&\n git -C libtmux-source checkout 2599d45369515aaf2fd5793bfe71cdc20de641b6 &&\n bundle config set --local path vendor/bundle &&\n bundle install &&\n sh run.sh bundle exec ruby connect.rb"
+ ],
+ "expectedOutput": "work",
+ "files": [
+ {
+ "name": "connect.rb",
+ "sha256": "b683160fe4caefea0e65c4670519a2bfaa6c4f16f402fc86c730a42df2943f5d"
+ },
+ {
+ "name": "Gemfile",
+ "sha256": "c0139fb5c549a7f4ccf72cf4ebf921de0497dbe9ea9a2e7c5756504ed5bf2f7c"
+ },
+ {
+ "name": "run.sh",
+ "sha256": "4d06c2acca46bb7ccf0a240924240c940ff6329bbd288376005b4a0e910dabf5"
+ }
+ ],
+ "tmux": [
+ "3.2a",
+ "3.7c"
+ ]
+ },
+ {
+ "port": "lua",
+ "language": "lua",
+ "sourceRevision": "5baa3f9b830ebdbc76fb50b5b3d7a5ad3f76d443",
+ "sourceRepository": "libtmux/libtmux-lua",
+ "toolchain": [
+ "Lua 5.5.1",
+ "luv 1.52.1-0"
+ ],
+ "page": "ports/lua/guides/attaching-to-tmux",
+ "shellRecipe": [
+ "git clone https://github.com/libtmux/libtmux-lua libtmux-source &&\n git -C libtmux-source checkout 5baa3f9b830ebdbc76fb50b5b3d7a5ad3f76d443 &&\n luarocks --tree ./rocks install luv 1.52.1-0 &&\n (cd libtmux-source &&\n luarocks --tree ../rocks make rockspecs/libtmux-scm-1.rockspec) &&\n eval \"$(luarocks --tree ./rocks path)\" &&\n sh run.sh lua connect.lua"
+ ],
+ "expectedOutput": "work",
+ "files": [
+ {
+ "name": "connect.lua",
+ "sha256": "4941e3f804521ec54e8692d83f6f3f22a0badb56affe4c24232ea28973fed2d6"
+ },
+ {
+ "name": "run.sh",
+ "sha256": "18b752e8b0c36ef899af7bb5d4454469b34e5a70f6cfd7939ef5efec13c931a1"
+ }
+ ],
+ "tmux": [
+ "3.2a",
+ "3.7c"
+ ]
+ }
+ ],
+ "rootExample": {
+ "file": "connect.sh",
+ "sha256": "ed88696309ac613caa271424dc4e1f52d2a6f18e4c1d5fe1ae01c385bc3dab1c",
+ "shellRecipe": "sh connect.sh",
+ "tmux": [
+ "3.2a",
+ "3.7c"
+ ],
+ "interactiveCommands": [
+ "tmux -L libtmux-demo -f /dev/null new-session -A -s work",
+ "tmux -L libtmux-demo list-sessions",
+ "tmux -L libtmux-demo attach-session -t '=work'",
+ "tmux -L libtmux-demo kill-session -t '=work'"
+ ]
+ }
+}