Commit 932e0ed

Eric Bower  ·  2026-03-23 11:21:28 -0400 EDT
parent 3b0e4e8
docs: cleanup and rewording

I'm removing `AGENTS.md` mainly because it is not particularly useful and there is research to
suggest these files don't really improve agent performance.
4 files changed,  +30, -74
+0, -47
......@@ -1,47 +0,0 @@
1-# zmx
2-
3-The goal of this project is to create a way to attach and detach terminal sessions without killing the underlying linux process.
4-
5-When researching `zmx`, also read the @README.md in the root of this project directory to learn more about the features, documentation, prior art, etc.
6-
7-## tech stack
8-
9-- `zig` v0.15.1
10-- `libghostty-vt` for terminal escape codes and terminal state management
11-
12-## commands
13-
14-- **Build:** `zig build`
15-- **Build Check (Zig)**: `zig build check`
16-- **Test (Zig):** `zig build test`
17-- **Test filter (Zig)**: `zig build test -Dtest-filter=<test name>`
18-- **Formatting (Zig)**: `zig fmt .`
19-
20-## find any library API definitions
21-
22-Before trying anything else, run the `zigdoc` command to find an API with documentation:
23-
24-```
25-zigdoc {symbol}
26-# examples
27-zigdoc ghostty-vt
28-zigdoc std.ArrayList
29-zigdoc std.mem.Allocator
30-zigdoc std.http.Server
31-```
32-
33-Only if that doesn't work should you grep the project dir.
34-
35-## find zig std library source code
36-
37-To inspect the source code for zig's standard library, look inside the `zig_std_src` folder.
38-
39-## find ghostty library source code
40-
41-To inspect the source code for zig's standard library, look inside the `ghostty_src` folder.
42-
43-## Issue Tracking
44-
45-We use bd (beads, https://github.com/steveyegge/beads) for issue tracking instead of Markdown TODOs or external tools.
46-
47-Run `bd quickstart` to learn how to use it.
+7, -0
......@@ -2,6 +2,13 @@
22
33 Use spec: https://common-changelog.org/
44
5+## Staged
6+
7+### Fixed
8+
9+- `zmx list` will send "no sessions found" to stderr instead of stdout
10+- `zmx wait` will send errors to stderr instead of stdout
11+
512 ## v0.4.2 - 2026-03-18
613
714 ### Changed
+13, -21
......@@ -15,7 +15,7 @@
1515
1616 ## features
1717
18-- Persist terminal shell sessions (pty processes)
18+- Persist terminal shell sessions
1919 - Ability to attach and detach from a shell session without killing it
2020 - Native terminal scrollback
2121 - Multiple clients can connect to the same session
......@@ -73,10 +73,10 @@ Commands:
7373 [r]un <name> [command...] Send command without attaching, creating session if needed
7474 [d]etach Detach all clients from current session (ctrl+\ for current client)
7575 [l]ist [--short] List active sessions
76- [c]ompletions <shell> Completion scripts for shell integration (bash, zsh, or fish)
7776 [k]ill <name> Kill a session and all attached clients
7877 [hi]story <name> [--vt|--html] Output session scrollback (--vt or --html for escape sequences)
7978 [w]ait <name>... Wait for session tasks to complete
79+ [c]ompletions <shell> Completion scripts for shell integration (bash, zsh, or fish)
8080 [v]ersion Show version information
8181 [h]elp Show this help message
8282 ```
......@@ -313,6 +313,8 @@ Host = d.*
313313 ControlPersist 10m
314314 ```
315315
316+Architecturally, `ssh` supports multiplexing multiple channels of communication within a single connection to a server. `ControlMaster` is the setting that tells `ssh` to multiplex multiple PTY sessions to a single server over one tcp connection. Neat!
317+
316318 Now you can spawn as many terminal sessions as you'd like:
317319
318320 ```bash
......@@ -322,9 +324,9 @@ ssh d.pico
322324 ssh d.dotfiles
323325 ```
324326
325-This will create or attach to each session and since we are using `ControlMaster` the same `ssh` connection is reused for every call to `ssh` for near-instant connection times.
327+Because the `attach` command is essentially an "upsert", this will create or attach to each session.
326328
327-Now you can use the [`autossh`](https://linux.die.net/man/1/autossh) tool to make your ssh connections auto-reconnect. For example, if you have a laptop and close/open your laptop lid it will automatically reconnect all your ssh connections:
329+Now you can use the [`autossh`](https://linux.die.net/man/1/autossh) tool to make your ssh connections auto-reconnect. For example, if you have a laptop and close/open your lid it will automatically reconnect all your ssh connections:
328330
329331 ```bash
330332 autossh -M 0 -q d.term
......@@ -345,6 +347,8 @@ ash d.dotifles
345347
346348 Wow! Now you can setup all your os tiling windows how you like them for your project and have as many windows as you'd like, almost replicating exactly what `tmux` does but with native windows, tabs, splits, and scrollback! It also has the added benefit of supporting all the terminal features your emulator supports, no longer restricted by what `tmux` supports.
347349
350+The end-game here would be to leverage your window manager's ability to automatically arrange your windows for each project with a single command.
351+
348352 ## socket file location
349353
350354 Each session gets its own unix socket file. The default location depends on your environment variables (checked in priority order):
......@@ -373,7 +377,7 @@ We are evaluating what should be configurable and what should not. Every configu
373377 ## known issues
374378
375379 - When upgrading versions of `zmx` where we make changes to the underlying IPC communication, it will kill all your sessions because it cannot communicate through the daemon socket properly
376-- Terminal state rehydration with nested `zmx` sessions through SSH: host A `zmx` -> SSH -> host B `zmx`
380+- Terminal state restoration with nested `zmx` sessions through SSH: host A `zmx` -> SSH -> host B `zmx`
377381 - Specifically cursor position gets corrupted
378382 - When re-attaching and kitty keyboard mode was previously enable, we try to re-send that CSI query to re-enable it
379383 - Some programs don't know how to handle that CSI query (e.g. `psql`) so when you type it echos kitty escape sequences erroneously
......@@ -404,31 +408,19 @@ In this way, `ghostty-vt` doesn't sit in the middle of an active terminal sessio
404408
405409 ## prior art
406410
407-Below is a list of projects that inspired me to build this project.
411+Below is a list of projects that inspired me to build this project. Architecturally, `zmx` uses aspects of both projects. For example, `shpool` inspired the idea of having libghostty restore the terminal state on reattach. Abduco inspired the idea of one daemon (and unix socket) per session.
408412
409413 ### shpool
410414
411-You can find the source code at this repo: https://github.com/shell-pool/shpool
415+https://github.com/shell-pool/shpool
412416
413417 `shpool` is a service that enables session persistence by allowing the creation of named shell sessions owned by `shpool` so that the session is not lost if the connection drops.
414418
415-`shpool` can be thought of as a lighter weight alternative to tmux or GNU screen. While tmux and screen take over the whole terminal and provide window splitting and tiling features, `shpool` only provides persistent sessions.
416-
417-The biggest advantage of this approach is that `shpool` does not break native scrollback or copy-paste.
418-
419419 ### abduco
420420
421-You can find the source code at this repo: https://github.com/martanne/abduco
422-
423-abduco provides session management i.e. it allows programs to be run independently from its controlling terminal. That is programs can be detached - run in the background - and then later reattached. Together with dvtm it provides a simpler and cleaner alternative to tmux or screen.
424-
425-### dtach
426-
427-You can find the source code at this repo: https://github.com/crigler/dtach
428-
429-A simple program that emulates the detach feature of screen.
421+https://github.com/martanne/abduco
430422
431-dtach is a program written in C that emulates the detach feature of screen, which allows a program to be executed in an environment that is protected from the controlling terminal. For instance, the program under the control of dtach would not be affected by the terminal being disconnected for some reason.
423+abduco provides session management (i.e. it allows programs to be run independently from its controlling terminal). Together with dvtm it provides a simpler alternative to tmux or screen.
432424
433425 ## comparison
434426
+10, -6
......@@ -750,6 +750,10 @@ fn wait(cfg: *Cfg, session_names: std.ArrayList([]const u8)) !void {
750750 var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
751751 const stdout = &stdout_writer.interface;
752752
753+ var stderr_buffer: [1024]u8 = undefined;
754+ var stderr_writer = std.fs.File.stderr().writer(&stderr_buffer);
755+ const stderr = &stderr_writer.interface;
756+
753757 // Highest match count seen so far. Lets us distinguish "sessions haven't
754758 // appeared yet" (keep polling) from "sessions we were tracking
755759 // disappeared" (fail -- daemon crashed or was killed).
......@@ -780,8 +784,8 @@ fn wait(cfg: *Cfg, session_names: std.ArrayList([]const u8)) !void {
780784 // is no longer deleted, so this session would otherwise
781785 // persist as task_ended_at==0 forever → infinite "still
782786 // waiting". Count it as done+failed so wait terminates.
783- try stdout.print("task unreachable: {s} ({s})\n", .{ session.name, session.error_name orelse "unknown" });
784- try stdout.flush();
787+ try stderr.print("task unreachable: {s} ({s})\n", .{ session.name, session.error_name orelse "unknown" });
788+ try stderr.flush();
785789 agg_exit_code = 1;
786790 done += 1;
787791 continue;
......@@ -806,8 +810,8 @@ fn wait(cfg: *Cfg, session_names: std.ArrayList([]const u8)) !void {
806810 // crashed and the remaining N-1 happen to be done, total==done
807811 // would be a false success.
808812 if (total < max_seen) {
809- try stdout.print("error: {d} session(s) disappeared before completing\n", .{max_seen - total});
810- try stdout.flush();
813+ try stderr.print("error: {d} session(s) disappeared before completing\n", .{max_seen - total});
814+ try stderr.flush();
811815 std.process.exit(1);
812816 return;
813817 }
......@@ -827,8 +831,8 @@ fn wait(cfg: *Cfg, session_names: std.ArrayList([]const u8)) !void {
827831 // typo, not a slow start.
828832 zero_match_iters += 1;
829833 if (zero_match_iters >= 3) {
830- try stdout.print("error: no matching sessions found\n", .{});
831- try stdout.flush();
834+ try stderr.print("error: no matching sessions found\n", .{});
835+ try stderr.flush();
832836 std.process.exit(2);
833837 return;
834838 }