npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@libtmux/workspace-cli

v0.1.0-alpha.12

Published

A tmuxp-compatible workspace manager for Node and Bun.

Readme

Workspace CLI

Manage tmux workspaces from YAML and JSON with Node.js 22.12+ or Bun 1.3.14+. tmux-workspace loads, captures, converts and imports tmuxp workspace files. This implementation is in development.

Install

Run the command without installing it. With Node:

$ npx -y @libtmux/[email protected] --help

With Bun. The command names node in its shebang, so bunx runs it on Node when Node is installed; --bun runs it on Bun:

$ bunx --bun @libtmux/[email protected] --help

With pnpm:

$ pnpm dlx @libtmux/[email protected] --help

With Yarn. Yarn refuses a release younger than its npmMinimalAgeGate, one day by default:

$ yarn dlx @libtmux/[email protected] --help

Or install it globally, which puts tmux-workspace on PATH. With npm:

$ npm install --global @libtmux/[email protected]

With pnpm:

$ pnpm add --global @libtmux/[email protected]

With Bun:

$ bun add --global @libtmux/[email protected]

The rest of this page runs the installed tmux-workspace. A runner passes the same arguments to the command when they follow the package name.

Load and capture

Save this document as workspace.yaml:

session_name: dev
windows:
  - window_name: editor
    layout: main-vertical
    panes:
      - vim
      - top

Load on a separate tmux socket:

$ tmux-workspace load ./workspace.yaml \
    -L workspace-cli-demo \
    -d \
    --json

Capture its live topology:

$ tmux-workspace freeze dev \
    -L workspace-cli-demo \
    --json

All native input layouts are checked before scripts run or sessions change. Names accept unique abbreviations supported by the running daemon. Only version-sensitive names query it; a cold endpoint uses the selected client. Saved layouts require a valid checksum, a nonempty tree and enough pane cells. tmux still owns geometry correction and pruning, and may reject a layout when it is applied. The CLI limits custom layouts to 8192 characters and 256 nested groups; these are application limits, separate from tmux's parser.

One default differs from tmuxp on purpose: a window that names no layout tiles its panes, where tmuxp stacks them by halving the last one. Explicit layout and focus agree with tmuxp, and so does the pane left active when nothing sets focus: the last pane created in the focused window.

@libtmux/workspace is a separate implementation, not this command's engine: it converges a session that already exists, and its strict schema rejects environment, before_script, window_index, suppress_history, x- keys and the other fields below. A document written here is not necessarily one that package accepts.

Loading reuses an existing session, and compares it against the document rather than rebuilding it: a window the document names that the session does not hold is reported, exit 1, and nothing is changed. Without -d, load attaches the final workspace or switches the current tmux client. Attachment uses the controlling terminal, including when standard streams are redirected; no terminal means failure before creating sessions. Inside tmux, attached load stays on the current server; use -d to load on another server. Explicit append adds windows to the current pane's session. A load that cannot finish a session it created removes it, so a rerun starts clean; an append keeps the windows it added and names them.

When clients share a session, tmux selects its most recently active client before loading. The CLI captures that client and targets the switch explicitly.

Two loads racing on the same session name are not coordinated: tmux offers no lock on a session that does not exist yet, so the loser's create can fail after the winner's session exists, or both can observe no session and each start one. Run concurrent loads against distinct session names.

load -2 forces 256-color terminal handling. Legacy -8 is rejected before calling tmux because supported tmux versions do not implement that legacy flag.

Pane readiness waits apply before sending commands, whatever the pane's shell is. Empty panes skip the wait, including with workspace_builder_options.pane_readiness: always.

Explicit window_index values reserve their slots before implicit windows are allocated from the effective base-index; null leaves the index unspecified. Append also reserves indexes from later input files. Removing the temporary bootstrap window preserves requested indexes and restores the prior local or inherited renumber-windows setting. A failed restoration appears as renumber_restore_error in the load result.

Native load rejects unsupported fields before any input runs scripts or changes tmux. The diagnostic includes the field path. For example, entter: false in a command is an error; use enter: false to leave that command unsubmitted. A key starting with x-, at any scope, is the exception: it is accepted, ignored at load, and left alone by convert. The refusal for any other unknown key names that prefix as the way to keep it.

| Scope | Accepted fields | | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Workspace | session_name, windows, start_directory, environment, shell_command_before, suppress_history, options, global_options, before_script, workspace_builder_options, plugins, workspace_builder, workspace_builder_paths, description | | Window | window_name, window_index, panes, start_directory, environment, shell_command_before, suppress_history, options, options_after, layout, window_shell, focus, description | | Pane | shell_command, shell_command_before, start_directory, environment, suppress_history, shell, focus, enter, sleep_before, sleep_after, description | | Command object | cmd, enter, sleep_before, sleep_after | | workspace_builder_options | pane_readiness |

description is neutral metadata at workspace, window, and pane scope. Option and environment maps retain their native names. Conversion preserves arbitrary fields without applying the native execution contract. Documents delegated to Python plugins or custom builders retain their extension fields; empty plugin lists and blank builder names still use native validation.

Inspect a workspace through MCP

The separate MCP server inspects loaded workspaces on the same tmux socket. After the detached load above, add it to your MCP client with that socket and the inspect toolset:

{
  "mcpServers": {
    "tmux": {
      "command": "npx",
      "args": ["-y", "@libtmux/[email protected]"],
      "env": { "LIBTMUX_SOCKET": "workspace-cli-demo", "LIBTMUX_TOOLSETS": "inspect" }
    }
  }
}

The socket is selected when the MCP server starts.

For a workspace loaded with -S, set LIBTMUX_SOCKET_PATH to the same absolute socket path instead of LIBTMUX_SOCKET. If the CLI uses TMUX_BIN, select that executable with the MCP server's LIBTMUX_TMUX_BIN too.

Discover tools with tools/list, then call list_sessions and list_panes. Call list_windows with session set to dev, the configuration's session name. Pass a returned pane ID as paneId to capture_pane or wait_for_text; bound output with maxLines and waits with timeoutMs. A pending text wait allows other inspection requests on the same connection. Read tmux://capabilities through resources/read to verify the selected socket and advertised tools. See the capture guide for incremental reads and cursors.

Closing the MCP subprocess's stdin cancels pending waits and joins its control connections. The loaded workspace remains available in tmux.

Imports

import tmuxinator and import teamocil translate source documents and validate native configuration fields before previewing or saving. Unknown fields, conflicting non-null aliases and malformed command shapes fail without replacing a destination. Generic convert preserves arbitrary fields.

Tmuxinator window command arrays stay in one pane. Explicit panes lists create separate panes. Project pre_window arrays form one ; -joined command; window pre arrays form one &&-joined command before each explicit pane. Window pre requires nonempty explicit panes. synchronize: true and before apply before sequential pane creation and command delivery; after applies afterward.

Teamocil commands arrays form one ; -joined command. Legacy cmd and splits are accepted. Window options apply before pane commands. The first requested window and pane focus wins; absent focus selects the first item. Both formats retain window order, pane order and layouts.

Imports save an absolute project root relative to the invocation directory, including when the source omits it. Window roots resolve relative to that root, so moving the saved file preserves working directories. Import requires neither tmux nor Python and does not check directory existence. Layout compatibility is checked against the selected tmux during load. Commands use native load's environment expansion from the invoking process before shell delivery.

Project lifecycle hooks, endpoint/runtime overrides, named pane titles, Teamocil clear, filters and pane widths are refused. Use an explicit native workspace or the source tool for those behaviors.

Tmuxinator expands ERB through Ruby before parsing; unexpanded <% markup in a Tmuxinator key or value fails before output or overwrite. Teamocil evaluates no templates, so <% in a Teamocil source is ordinary text and is preserved.

Output

Every command accepts --json and --ndjson before or after the command name. NDJSON takes precedence when both are present. Machine load requires -d or --append. Machine commands never prompt. Human output honors --color and NO_COLOR; machine output contains encoded data without terminal styling.

convert, import commands, and machine freeze return a document when no destination is given. --save-to writes a file; --force permits replacement. The file format is separate from the JSON/NDJSON output mode. freeze infers JSON from a .json destination and otherwise saves YAML; -f overrides the inferred format. --quiet suppresses its human save message and preserves machine results. Capture accepts an exact session name or ID. Without one, it uses the authenticated current pane's session or the sole session on the selected server. Several sessions outside tmux require an explicit target. Capture reads honor cancellation. Saves check for cancellation before publishing; publication already in progress may still complete.

Human load shows progress on terminal stderr. --progress-format accepts default, minimal, window, pane, verbose, or a template with tokens such as {session}, {window}, and {session_pane_progress}. Pane counts advance after configured commands are sent; they do not wait for those commands to exit. --progress-lines controls the recent script-output panel: default 3, 0 forwards each stream directly, and -1 uses the available terminal height. Retention is bounded to 64 KiB of characters for completed lines and each partial stream. TMUXP_PROGRESS_FORMAT and TMUXP_PROGRESS_LINES set defaults; explicit flags take precedence. --no-progress, TMUXP_PROGRESS=0, a dumb terminal, or redirected stderr disables the display. Frames update from build events without timing sleeps, clip to terminal width, and clear before results or attachment.

--log-level filters diagnostics on stderr: debug, info, warning (default), error, or critical. Machine diagnostics are NDJSON. load --log-file appends the selected diagnostics as NDJSON, including script output at info level. New log files use owner-only permissions. Nonregular destinations are rejected before session creation; existing log contents are preserved. If logging fails during execution, stderr reports log_error and the command keeps its result and exit status. Cancellation closes the owned log handle. Interrupting a blocked output write still joins owned children and exits 130. An interrupted load allows up to 100ms to emit its final machine result before exiting; an unread pipe cannot hold the process open. Cancelled output may be incomplete.

Bootstrap and editor commands use quoted argv directly. Child output is UTF-8 with replacement for invalid bytes. Captured results retain at most 64 KiB of source bytes per stream and report truncation; NDJSON forwards output as it arrives. A nonzero editor status becomes the CLI exit status. After a captured child exits, its streams have 100ms to drain before owned descendants are stopped. Remaining capture streams close after group cleanup.

Python compatibility

Search uses native JavaScript regular expressions. -F treats patterns as literal text; -w applies whole-word matching to the complete expression. Python-specific regex syntax is outside the native search contract.

shell uses an interpreter with tmuxp 1.74.0 installed. Set TMUX_WORKSPACE_PYTHON to choose its executable; otherwise it uses python3. Code passed with -c runs in tmuxp's Python context. An interactive shell needs a controlling terminal.

load uses the same optional interpreter when a workspace selects Python plugins or a workspace_builder. Ordinary workspaces, empty plugin lists, and blank builder names use the native builder without starting Python. workspace_builder_paths adds existing import directories relative to the workspace file. These directories contain executable extension code.

The adapter expands common configuration fields and supplies the tmuxp builder protocol. Explicit custom builders may omit windows and consume their own configuration. Append retains the authenticated current session across input files. Python extension append rejects before_script; run that script separately. Native append retains its bootstrap support.

Extension output uses the bounded child streams described above. Human progress shows a workspace label and script output without native pane counters. Extension results report effects_scope: "observed", effects_unknown: true, and newly observed window and pane IDs. These observations include concurrent changes and do not establish ownership; failures do not roll back opaque extension effects. Cancellation joins the owned Python process group, then allows up to one second to observe surviving topology. A changed daemon or failed observation is reported without comparing IDs across daemon lifetimes.

Reference and completion

The command reference and command metadata come from the native parser. Regenerate them after changing command definitions:

$ bun run --cwd packages/workspace-cli docs:generate

The build checks that the reference and packaged completion scripts are current. The scripts complete commands, options, enum values, and file paths without starting Node, Bun, or tmux. Saved workspace names and live sessions are outside their completion scope.

Completion is tested on Linux with Bash 5.2, Zsh 5.9, and fish 4.8. Bash 3.2 is a syntax target; older shells and filenames containing newlines remain unverified.

With tmux-workspace on PATH, load the script for your shell. Bash:

$ source <(tmux-workspace completion bash)

Zsh, with its completion system initialized:

$ autoload -Uz compinit && compinit && source <(tmux-workspace completion zsh)

fish:

$ tmux-workspace completion fish | source

For automation, --json and --ndjson wrap the script in a structured result.

Build from source

Run from the repository root:

$ bun run --cwd packages/libtmux build
$ bun run --cwd packages/workspace-cli build
$ node packages/workspace-cli/dist/main.js --help

Development status

Native discovery, conversion, imports, common searches, detached load, append, capture, editor invocation, diagnostics, and the Python shell have executable tests. The Linux installed-package check packs the CLI and core, installs both offline into an empty project, and exercises every command in JSON and NDJSON on Node 22 and Bun. It needs the optional Python runtime described above:

$ bun run --cwd packages/workspace-cli test:install

Plugin and custom-builder execution has output, cancellation and borrowed-state tests using the optional interpreter. Confirmation and destination choices use explicit command-line flags. Native process and terminal evidence covers Linux; macOS coverage is a package canary. Benchmark results cover isolated Linux fixtures.

The core library keeps its zero-runtime-dependency contract. Commander, YAML, and terminal text helpers are bundled into this CLI; libtmux remains a separate package. Machine commands skip loading the progress renderer.