Repository navigation
zeropty 0.1.0
The first public version: a session engine for programs that drive many terminal programs (coding agents, scripts, test harnesses), which accepts tmux's command format without a PTY. macOS only.
Known limitations: macOS only (kqueue, the DYLD shim). One program per session, so there are no windows, panes, status line, copy mode, key bindings or configuration file. wait-pane is not available as an embedded function call, only over the socket (including an embedded listen). Programs protected by SIP or signed with the hardened runtime cannot load the shim, so zeropty-tty runs a re-signed copy instead, and setuid or entitlement programs (top, ps) do not work. End of file with C-d closes the session's input (a pipe has no one-time EOF). Before 1.0, a MINOR release may break the interfaces in docs/VERSIONING.md.
Added
- Server and CLI (
zeropty), with the same arguments as tmux:new-session,has-session,kill-session,list-sessions,kill-server,send-keys(key names compared byte for byte with tmux 3),capture-pane(-p -e -J -N -S -E -b -q),load-buffer,paste-buffer,display-message,pipe-pane,rename-session,resize-window/resize-pane,set-environment -g/show-environment -g,attach-session(-rread-only),-V, and 25 tmux format variables. Unknown flags are errors. The firstnew-sessionstarts the server. - Commands tmux does not have:
wait-pane(waits for text or for quiet without polling),grep-panes(searches the screens of all sessions),pull-sessions/push-sessions(moves running sessions to another process without touching the programs),restart-server(upgrades the server without ending sessions). - No PTY: a session's program talks to pipes, and the engine emulates the line discipline (Enter, the
C-csignal, line editing, echo,C-d). For programs that need a terminal (claude, vim, less, python),shim.dylib,claude-zeroptyandzeropty-ttyanswerisatty,tcgetattr, the window size and/dev/tty. - Screen emulator: 6 bytes per cell, compared with the
vt100crate (0.16) on recorded claude sessions and random input. Quiet screens are folded away and later compressed with zstd. - Socket protocol: one JSON line per request and per reply on
~/.zeropty/mux.sock(ZEROPTY_SOCK), an optional raw reply format, and attach frames. - Embedding: the C ABI (
libzeropty_c.dylib,zeropty.h:zeropty_open,zeropty_command,zeropty_listen, andzeropty_open_polled/zeropty_fd/zeropty_timeout_ms/zeropty_processfor polled use) and the Node module (zeropty.node,client/embedded.mjs) run the engine inside your process.listenserves the same socket protocol from the host process. - Clients:
client/zeropty.mjs(Node, keeps one connection open), and a zsh function (shell/zeropty.zsh) and a bash builtin (shell/zeropty_bash.c) that talk to the socket without starting a process. - Environment variables
ZEROPTY_SOCK,ZEROPTY_HISTORY,ZEROPTY_COALESCE_MS,ZEROPTY_FREEZE_MS,ZEROPTY_COLD,ZEROPTY_COLD_DIR,ZEROPTY_RECORD(docs/USAGE.md). - Benchmarks measured interleaved with tmux 3.7c on one Mac mini M1 (docs/BENCHMARKS.md): 11 of the 14 measured items are at 10x or better, and 3 are not there yet (memory with 400 idle sessions on the standalone server at 6.2x, and
send-keysagainst tmux control mode at 810x embedded and 2.22.8x over the socket). The reasons are in that document. - A plugin system (docs/PLUGINS.md): plugins receive session create, exit, rename and output events and add new commands. The Rust
Plugintrait (API_VERSION1),plugin-attachover the socket (any language, out of process), C callbacks withzeropty_add_plugin, dylib plugins withzeropty plugin-run/zeropty_load_plugin, an opt-in autostart directoryZEROPTY_PLUGIN_DIR, and a status check withlist-plugins. With no plugins there is no cost, and a plugin that panics, hangs or is slow cannot hold up the engine. - Command calls that do not wait: C
zeropty_command_start,zeropty_call_poll,zeropty_call_due_ms,zeropty_call_free, and NodeEngine.commandStart. zeropty --version: the same output aszeropty -V(zeropty 0.1.0). The zsh function, the bash builtin andclient/zeropty.mjsalso answer it by running the program, as they do for-V.- The format variable
#{version}: the version of the running server (or embedded engine). Adisplay-message -pthat uses only this variable works without a session (zeropty display -p '#{version}'). - C ABI:
ZEROPTY_VERSION_MAJOR,_MINOR,_PATCH,_PRERELEASE,_IS_PRERELEASE,_STRINGand_NUMBERinzeropty.h, andzeropty_version(), which returns the version of the linked library.ZEROPTY_VERSION_NUMBERdoes not include the prerelease, so0.2.0-rc.1and0.2.0have the same value; tell them apart withZEROPTY_VERSION_IS_PRERELEASE. - Node:
version()inzeropty.nodeandclient/embedded.mjs, andVERSIONinclient/zeropty.mjsandclient/embedded.mjs. - The versioning policy
docs/VERSIONING.md: the compatibility surface whose changes count as breaking (the CLI's arguments, output and exit codes, the socket protocol, the session record, the C ABI, the Node API, environment variables,dist/names) and the MSRV (Rust 1.88). Before 1.0, a MINOR release may break this surface, and such changes are announced as**BREAKING**entries. zeropty --help(-h,help) prints the list of commands and options, andzeropty help <command>prints that command's usage and a link to its documentation. It answers without a server.- Documentation: a reworked Korean README, a documentation index (
docs/README.md), a FAQ, runnable examples (examples/), and rustdoc with examples for every public Rust item.
Fixed
- The CLI no longer panics when the reader closes the pipe early (
zeropty capture-pane -p ... | head -1).
CHANGELOG · 한국어 · Versioning