
Show HN: Libtmux-ts 0.1 – tmux library for Node.js, TypeScript, bun
Typed control of tmux for Bun and TypeScript — immutable snapshots, declarative queries, and a library that ships zero runtime dependencies.
以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。
libtmux for TypeScript
Typed control of tmux for Bun and TypeScript — immutable snapshots, declarative queries, and a library that ships zero runtime dependencies.
Quickstart • Querying • Packages • API reference • Hosted API reference • Examples • Changelog
Releases are numbered 0.1.N, and each release increments N. The API is not settled: any release may change or remove exported identifiers, and the changelog marks each break. Pin an exact version to avoid one. Read the changelog before you upgrade.
Yes, if you drive tmux from a program — an agent that runs commands and reads what they printed, a workspace launcher, a test harness, a dashboard — and you want the terminal's state as typed data rather than as parsed strings.
Probably not, if you want a .tmux.conf generator or a TUI. This library controls a running server; it does not write your tmux configuration.
The idea in one line: read the whole server once into an immutable snapshot, then query it like data.
$ bun add libtmux
npm, pnpm, yarn $ npm i libtmux
$ pnpm add libtmux
$ yarn add libtmux
Requires Bun 1.3.14+ or Node 22+, and tmux 3.3a or newer.
Runs on Linux, including WSL. macOS is supported on a best-effort basis, and CI coverage for it is highly limited.
import { Server } from "libtmux";
const server = new Server(); const snapshot = await server.snapshot();
// No further tmux calls: everything below resolves against the snapshot. const editors = snapshot.panes.where({ currentCommand: "vim" }); console.log(editors.count(), editors.at(0)?.session?.name);
Building something rather than reading it looks like this — and this block is a literal excerpt of examples/quickstart/quickstart.ts, which the integration suite runs against a real tmux server:
const session = await server.newSession({ name: "quickstart" }); const editor = await session.newWindow({ name: "editor" }); await editor.split();
const snapshot = await server.snapshot();
const found = snapshot.windows.where({ name: "editor" }).one();
const paneCount = found.panes.length;
What querying looks like
This is the part worth judging the library on. .where() takes declarative, serializable criteria; .filter() takes an ordinary predicate. They are never overloaded into each other.
// Equality, string operators, AND/OR/NOT, and regular expressions as data. snapshot.sessions.where({ AND: [ { name: { startsWith: "prod" } }, { windows: { some: { name: { regex: { pattern: "^log", flags: "" } } } } }, ], });
// Quantifiers over relations: some / every / none, and is / isNot. snapshot.windows.where({ session: { is: { name: "work" } } });
// Case-insensitive when you ask for it. snapshot.sessions.where({ name: { contains: "API", mode: "insensitive" } });
A Selection is immutable, ordered, replayable, and Iterable. It is not an Array:
selection.length; selection.at(-1); selection.toArray(); [...selection];
selection.one({ name: "work" }); // throws NoMatchError / MultipleMatchesError selection.oneOrUndefined({ name: "work" }); selection.exists({ name: "work" });
Criteria are data. encodeWhereDocument writes a model-tagged query; decodeWhereDocument validates one read from a config file, MCP call, or CLI.
The library, MCP server, workspace builder, and workspace CLI share one release version.
The library: Server, Session, Window, Pane and Client handles over an immutable snapshot of tmux.
$ bun add libtmux
import { Server } from "libtmux";
const server = new Server(); const session = await server.newSession({ name: "work" }); const editor = await session.newWindow({ name: "editor" }); await editor.split();
await editor.panes.at(0)?.sendKeys("echo hello"); const lines = await editor.panes.at(0)?.capture();
Read next: Snapshots · Querying · Operations · Watching · Recipes · Errors · API reference
A stdio MCP server. Point it at a socket and an agent can list sessions, read a pane, send keys, and wait for output rather than polling for it.
With no configuration it starts its own tmux server on the libtmux-mcp socket:
$ npx -y @libtmux/mcp@0.1.0
Add it to any MCP client — this is the whole configuration:
{ "mcpServers": { "tmux": { "command": "npx", "args": ["-y", "@libtmux/mcp@0.1.0"], "env": { "LIBTMUX_SOCKET": "agent" } } } }
Claude Code, in one command
$ claude mcp add tmux --env LIBTMUX_SOCKET=agent --
npx -y @libtmux/mcp@0.1.0
The tools an agent reaches for first:
The tools are split into inspect, manage, execute, and teardown. tmux://capabilities is the only resource; the server exposes no prompts, resource templates, background jobs, or generic mutation tools.
Read next: Why it exists · Configuration · Toolsets and trust · Choosing the right tool
Describe a session; apply it. Applying twice converges rather than duplicating.
$ bun add @libtmux/workspace libtmux
import { Server } from "libtmux"; import { applyWorkspace } from "@libtmux/workspace";
const server = new Server();
await applyWorkspace(server, { session_name: "api", windows: [ { window_name: "editor", panes: ["vim", "git status"] }, { window_name: "server", panes: [{ shell_command: "bun dev", focus: true }] }, ], });
Read next: The config shape · Converging
tmux-workspace loads, captures and converts tmuxp workspace files on Node or Bun. Ordinary loads start no Python.
session_name: dev windows:
- window_name: editor layout: main-vertical panes:
- vim
- top
Load it detached, on a socket of its own:
$ npx -y @libtmux/workspace-cli@0.1.0 load ./workspace.yaml
-L workspace-cli-demo
-d
Read next: Install · Load and capture · Command reference
Four programs covering acquisition, control-mode watching, the act-then-wait loop an agent needs, and building a workspace. Each is executed by the integration suite, so the code there is the code that runs.
$ bun test examples
How work is arranged
Observation, planning and concurrency compose around the same command engine — the full table is here.
Creating twelve windows costs, each figure including the one query that reads the result back:
$ bun packages/libtmux/scripts/bench-modes.ts
What this project promises
The library ships zero runtime dependencies. A test holds it. @libtmux/mcp and @libtmux/workspace declare their own.
Real tmux, every commit. CI runs the suite against every tmux release from 3.3a to 3.8 — no mocks stand in for a server.
Documentation is a gate. Every public symbol carries a compiled example, the API reference is generated from the source that implements it, and every link, install command and recipe on this page is checked on each run.
Softly tracks Python libtmux. Names and shapes follow 0.62.0 where TypeScript agrees with them; each departure is a decision a gate holds to the code.
Repository
packages/libtmux the library packages/mcp the MCP server packages/workspace the workspace builder packages/workspace-cli the tmux-workspace command examples runnable examples, used as tests attic reference notes
Working here: AGENTS.md routes to the policy that governs a change. The layout and the change discipline live there; how we write is in .github/WRITING.md, and the gates, real tmux, and releases are in .github/CONTRIBUTING.md.
Please use the following BibTeX template to cite libtmux-ts in scientific discourse:
@misc{libtmux-ts, author = {Tony Narlock}, year = {2026}, note = {https://libtmux.org/en/ts/}, title = {libtmux-ts: TypeScript wrapper for tmux} }
License
MIT — a port of tmux-python/libtmux.
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。