From 0d7047ccfc0aa8cf0342381de41fc63883f2c7e6 Mon Sep 17 00:00:00 2001 From: phall1 Date: Wed, 3 Jun 2026 02:58:41 -0400 Subject: [PATCH 1/3] =?UTF-8?q?docs:=20full=20reposition=20=E2=80=94=20pee?= =?UTF-8?q?r=20thesis,=20deadpan=20voice,=20truth-up=20the=20status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reframe the user-facing docs around the real thesis: a human's TUI and an agent's API are peer consumers of the same terminal, by design. Lead with the payoff, not the mechanism (libghostty moves to an aside); undersell on purpose; hold competitor shots until there's proof. - README: new hook, demo placeholder front-and-center, headless/agent surface above the fold, honest stable/moving/not-yet split. - docs/INSTALL.md (new): honest install ergonomics — brew + source, no cargo-install (binary unpublished), platform matrix. - CONCEPTS: peer thesis in the TL;DR; fix stale "agent SDK planned" to reflect the shipped CLI + phux-mcp surface vs the still-future crate. - vision: lighter framing; same agent-surface truth-up. - docs/README.md: real front door (read-order + pick-a-lane) over a bare index; add INSTALL/agents/mcp/web. - QUICKSTART: correct the stale "only phux + phux server ship" — the headless verbs and MCP adapter are real now. just docs-check green (76 files, 0 violations). All links verified. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 220 +++++++++++++++++++++------------------------ docs/CONCEPTS.md | 12 +-- docs/INSTALL.md | 91 +++++++++++++++++++ docs/QUICKSTART.md | 36 +++++--- docs/README.md | 61 ++++++++----- docs/vision.md | 35 +++++--- 6 files changed, 284 insertions(+), 171 deletions(-) create mode 100644 docs/INSTALL.md diff --git a/README.md b/README.md index b247bd7..18b0c7b 100644 --- a/README.md +++ b/README.md @@ -6,170 +6,154 @@ last-reviewed: 2026-06-03 # phux -**A terminal multiplexer that doesn't re-parse your terminal.** +*A terminal multiplexer. It's pronounced however you just pronounced it.* -tmux and screen sit in the middle of the wire and re-interpret every byte your -programs emit — which is why kitty graphics, undercurl, pixel-precision mouse, -and half of OSC degrade or vanish across a detach/reattach. phux runs the *same* -VT parser ([libghostty][lghv]) on **both ends** of the connection, so modern -terminal protocols survive a remote attach losslessly. Structurally — not "we -patched the common cases." +It does roughly what tmux does. You attach, you split some panes, you +detach, your shell keeps running, you come back later and it's all still +there. So far, not a sales pitch. -And the thing it multiplexes isn't a *session* or a *pane*. It's a **terminal**: -a first-class object you can spawn, observe, drive, and address — by hand from a -tmux-shaped TUI, or headless from a script or an agent over a typed wire, on this -box or across a fleet. +Two things are a little different. -```sh -phux # attach (auto-starts a server) — the tmux-shaped TUI -phux ls --json # list sessions, machine-readable -phux send-keys build:0.1 'cargo test' Enter -phux run build "cargo test" # run in a real pane, get the exit code back -phux wait build --until "0 failed" # block until output matches, then exit 0 -phux watch --json work:1.0 | jq . # live event stream: bells, titles, dirty/idle, lifecycle -``` +**One.** The modern terminal stuff — kitty graphics, truecolor, hyperlinks, +the works — doesn't fall apart when you detach and reattach. It just keeps +working. Boring, on purpose. - +**Two.** An AI agent can drive the same terminal you're looking at. Not a +screenshot of it. Not a scrape. The actual terminal, over an actual API. You +didn't have to turn on a "robot mode," because there isn't one. There's just +terminals — and some of the things attached to them happen to be people. -## What actually works today +That second one is doing more than it lets on. We'll get to it. -Two surfaces ride the same source-of-truth libghostty `Terminal`: + -- **The TUI** — auto-attach/detach/re-attach, multi-pane splits, status bar, - keybindings, multi-client attach. Full modern-protocol passthrough (Kitty - keyboard, truecolor, OSC 8 hyperlinks, OSC 133, images) because the parser is - identical on both ends. -- **The headless / agent surface** — every command above is real and tested. - A selector grammar (`name`, `name:window.pane`, `@id`, `.` focused, `=` - last-focused) addresses any pane; reads are side-effect-free (no attach, no - resize) and take `--json`. Same surface is exposed over MCP by the `phux-mcp` - crate — six tools, JSON-RPC over stdio, no protocol privilege. **This is the - part you point an agent at.** +## Try it -Both are real binaries you can run now, not a roadmap. The honest line on what's -stable vs. still moving is in [Status](#status). +```sh +brew install phall1/phux/phux +phux +``` -## The idea +That's the whole thing. You're attached. Detach with `Ctrl-A d`; the server +keeps your shell alive. Type `phux` again and you're right back where you were. -Two decisions do all the work. +(No Homebrew, or you want to hack on it? [Install from source](./docs/INSTALL.md) +— it's a Nix dev shell and one `cargo run`.) -**The same parser on both ends.** The server's libghostty `Terminal` is -canonical; the client's is a local mirror for rendering. Nothing in the middle -re-parses VT — so new terminal features light up on the next libghostty bump, on -both ends, for free. Older multiplexers re-parse mid-path and degrade fidelity. -phux structurally can't. +## The part the GUI doesn't show you -**The terminal is the unit, not the session.** Sessions, windows, panes, splits -— the whole tmux vocabulary — live in the TUI's metadata layer, never on the -wire. An agent speaks to *terminals* and never hears the word "window." That's -why a non-human consumer is a first-class citizen instead of a screen-scraping -hack bolted onto a tool built for one human at a keyboard. +Everything above also works without a TTY. Same terminals, addressed by name +or id, driven from a script — or an agent — with clean JSON coming back: -The wire is layered — L1 Terminal / L2 Collection / L3 Metadata — and consumers -declare which tiers they speak so the server omits the rest. Identity is -federation-ready from byte zero: `TerminalId` is `LOCAL{id} | SATELLITE{host,id}`. -v0.1 constructs `LOCAL`; the wire already accepts `SATELLITE`; v0.2 routes it; -the bytes never change. Full mental model: [`docs/CONCEPTS.md`](./docs/CONCEPTS.md). +```sh +phux ls --json # what's running +phux send-keys build:0.1 'cargo test' Enter +phux run build "cargo test" # runs in a real pane, hands you the exit code +phux wait build --until "0 failed" # blocks until the output shows up, exits 0 +phux watch --json work:1.0 | jq . # live events: bells, titles, idle, lifecycle +``` -[lghv]: https://github.com/Uzaaft/libghostty-rs +Point an MCP-speaking agent at it instead and it gets the same six verbs as +tools (`phux-mcp`, JSON-RPC over stdio). The agent isn't peeking at a human's +session through a keyhole. It's a first-class user with the same keys. -## Install +## Why it's built this way -Homebrew (macOS, Linux x86_64): +Old multiplexers were built for one person at one keyboard, because that was +the only kind of user there was. Fair enough at the time. -```sh -brew install phall1/phux/phux -``` +Now some of the users are programs. A program doesn't want a screen to read +pixels off of — it wants to start a thing, find out when it's done, and read +the exit code. The funny part: if you build the terminal so a program can use +it cleanly, the human gets a better deal too. Nothing re-interprets your bytes +in the middle, so nothing gets mangled on the way through. -From source — the toolchain is Nix-pinned (Rust 1.90 + Zig for libghostty's -build), so the dev shell is the supported path: +So phux makes the **terminal** the thing — not the "session," not the +"window." A human's TUI and an agent's API are just two ways to hold the same +object, and neither one is the real customer. -```sh -nix develop # or: direnv allow -cargo run --bin phux # auto-spawns a server and attaches -``` +> Under the hood: the same terminal engine ([libghostty][lghv]) runs on both +> ends of the wire, so bytes pass straight through instead of getting re-parsed +> and degraded. That's the unglamorous reason the fancy stuff survives a +> reattach. You don't have to care about it. It just means things work. -Detach with the default prefix, `Ctrl-A d`. Full walk-through: -[`docs/QUICKSTART.md`](./docs/QUICKSTART.md). +The longer version, with the boxes-and-arrows: [`docs/CONCEPTS.md`](./docs/CONCEPTS.md). -## Status +[lghv]: https://github.com/Uzaaft/libghostty-rs -**Stable, shipped — L1 terminal control plane:** -- TUI: attach / detach / re-attach, multi-pane splits, status bar, keybindings, - multi-client attach -- libghostty wire protocol (VT bytes server→client, structured input - client→server), version-negotiated. `phux-protocol` is the only crate - published to crates.io. +## What actually works today + +It's v0.1. Calibrate accordingly, then get pleasantly surprised. + +**Solid, won't move under you:** +- The TUI — attach / detach / re-attach, multi-pane splits, status bar, + keybindings, multiple clients on one session +- Full modern-protocol passthrough (Kitty keyboard, truecolor, OSC 8, OSC 133, + images), because the parser is identical on both ends +- The wire itself, version-negotiated. `phux-protocol` is the one crate on + crates.io. -**Shipped and tested, API still moving (pre-1.0):** -- Headless verbs: `ls`, `snapshot`, `send-keys`, `run`, `wait`, `watch`, `new`, - `kill`, `rename`, `config` — selector grammar + `--json` -- `phux-mcp`: MCP adapter exposing six tools over JSON-RPC stdio -- L2 agent protocol layer (collections, terminal-state reads, event subscriptions) +**Real and tested, but the API may still wiggle before 1.0:** +- The headless verbs above: `ls`, `snapshot`, `send-keys`, `run`, `wait`, + `watch`, `new`, `kill`, `rename`, `config` +- `phux-mcp` — the same surface as MCP tools -**Not yet — addressing is in the wire, routing is not:** -- Control-plane routing across satellites (`SATELLITE{host,id}` ids are accepted - today, not yet routed) -- Native GUI consumer over libghostty's surface API +**Designed, addressed-for, not wired yet:** +- Driving terminals across machines. The wire already speaks + `SATELLITE{host, id}`; nothing routes it yet. v0.2. +- A native GUI consumer, a typed Rust SDK crate, predictive local echo. -Building against it now? L1 is the part that won't move under you. Contributor -roadmap + constraints: [`CONTRIBUTING.md`](./CONTRIBUTING.md). +If it's not in one of those first two lists, it's a promise, not a feature. +We try to keep that line honest. -## Where to go next +## Where to go from here | You want to | Read | |---|---| -| Understand the model | [`docs/CONCEPTS.md`](./docs/CONCEPTS.md) | -| Run it | [`docs/QUICKSTART.md`](./docs/QUICKSTART.md) | -| Customize config and keybindings | [`docs/CONFIG.md`](./docs/CONFIG.md) | +| Get it on your machine | [`docs/INSTALL.md`](./docs/INSTALL.md) | +| First session, start to finish | [`docs/QUICKSTART.md`](./docs/QUICKSTART.md) | +| Actually understand it | [`docs/CONCEPTS.md`](./docs/CONCEPTS.md) | | Drive it from an agent | [`docs/consumers/agents.md`](./docs/consumers/agents.md) · [`docs/consumers/mcp.md`](./docs/consumers/mcp.md) | +| Customize keys and config | [`docs/CONFIG.md`](./docs/CONFIG.md) | | Read the wire spec | [`docs/spec/`](./docs/spec/) | -| Understand how it's built | [`docs/architecture/`](./docs/architecture/) | -| Read the TUI surface | [`docs/consumers/tui.md`](./docs/consumers/tui.md) | -| Read the long arc | [`docs/vision.md`](./docs/vision.md) | +| See how it's built | [`docs/architecture/`](./docs/architecture/) | +| Read where it's going | [`docs/vision.md`](./docs/vision.md) | | See past decisions | [`ADR/README.md`](./ADR/README.md) | -| Contribute | [`CONTRIBUTING.md`](./CONTRIBUTING.md) | - -The doc system itself is defined in [`docs/CONVENTIONS.md`](./docs/CONVENTIONS.md) -— frontmatter schema, TL;DR rule, ADR template, CI gates. +| Build it with us | [`CONTRIBUTING.md`](./CONTRIBUTING.md) | ## Crates -| Crate | Purpose | +| Crate | Does | |---|---| -| `phux` | Binary; `attach`/`server` + the headless verbs | -| `phux-protocol` | Wire types, codec, version negotiation. The only crate intended for publication | +| `phux` | The binary: `attach` / `server` plus the headless verbs | +| `phux-protocol` | Wire types, codec, version negotiation. The only one meant for publishing | | `phux-core` | Domain types: in-process terminal / collection registries | -| `phux-server` | Daemon: per-terminal actor, PTY supervision, output fanout | +| `phux-server` | The daemon: per-terminal actor, PTY supervision, output fanout | | `phux-client-core` | Renderer + protocol client, ratatui-free (the boundary is compiler-enforced) | -| `phux-client` | TUI chrome (ratatui) over `phux-client-core` | +| `phux-client` | The TUI chrome (ratatui) over `phux-client-core` | | `phux-config` | TOML config schema + status widget contract | -| `phux-mcp` | MCP adapter: the agent CLI surface over JSON-RPC stdio | - -Future, not yet started: `phux-client-gui` (native GUI consumer over libghostty's -surface API). +| `phux-mcp` | The agent surface as MCP tools, over JSON-RPC stdio | -## Non-goals +## Things phux deliberately won't do -Each of these is a "no" that keeps the substrate honest, not a feature deferred: +Each of these is a "no" that keeps the thing honest, not a feature we forgot: -- **No embedded scripting language.** Commands are typed IPC messages. Logic that +- **No embedded scripting language.** Commands are typed messages. Logic that wants a runtime can shell out to one. -- **No plugin host.** Hooks are typed events. A plugin contract, if it ever lands, - comes after we know what is genuinely pluggable. -- **No copy-mode reinvention.** Selection and extraction belong to libghostty and - the host terminal. phux owns exactly one primitive libghostty doesn't provide: - literal search over scrollback. +- **No plugin host.** Hooks are typed events. A plugin contract, if it ever + shows up, comes after we know what's actually pluggable. +- **No copy-mode reinvention.** Selection belongs to your terminal. phux owns + exactly one primitive nobody else provides: literal search over scrollback. - **No homegrown crypto.** SSH and Unix-socket permissions are the trust model. - **No format-template DSL.** The status bar takes typed widgets, not a printf - dialect to maintain forever. + dialect we'd have to maintain until the heat death of the universe. -Full rationale in [`CONTRIBUTING.md`](./CONTRIBUTING.md). +Full reasoning: [`CONTRIBUTING.md`](./CONTRIBUTING.md). ## License -Dual-licensed under [MIT](./LICENSE-MIT) or [Apache-2.0](./LICENSE-APACHE) at your -option. +Dual-licensed under [MIT](./LICENSE-MIT) or [Apache-2.0](./LICENSE-APACHE), your +call. diff --git a/docs/CONCEPTS.md b/docs/CONCEPTS.md index ee9f8d5..6b642cc 100644 --- a/docs/CONCEPTS.md +++ b/docs/CONCEPTS.md @@ -1,18 +1,18 @@ --- audience: humans, contributors, agents, consumers stability: stable -last-reviewed: 2026-05-28 +last-reviewed: 2026-06-03 --- # Concepts -**TL;DR.** phux is a libghostty-backed terminal control plane. The terminal is the model: spawned, observed, controlled, persisted, addressable across hosts. Sessions, windows, panes are the TUI's way to arrange them. The wire is layered; federation is in the addressing, not bolted on. +**TL;DR.** The thing phux manages is the *terminal* — spawned, observed, driven, persisted, addressable across hosts — not the session or the pane. A human's TUI and an agent's API are two consumers of the same terminal, and neither is privileged. Sessions, windows, and panes are just how the TUI arranges terminals for a person. The wire is layered (L1/L2/L3) so a consumer subscribes to only what it needs; identity is federation-ready from the first byte. --- ## Terminals, not panes -A terminal: runs a process, parses bytes into a grid, accepts structured input, reports events (title, cwd, command lifecycle, hyperlinks, bells). +A terminal: runs a process, parses bytes into a grid, accepts structured input, reports events (title, cwd, command lifecycle, hyperlinks, bells). That object is the whole model. A person looking at it and a program driving it are holding the same thing two different ways — that equivalence is the point, not a feature bolted on. Sessions, windows, panes, splits — the entire tmux vocabulary — live in the TUI layer. Not on the wire. Not load-bearing for agents or control planes. @@ -96,7 +96,7 @@ See [ADR-0007](../ADR/0007-mosh-class-transport-and-satellites.md) and [ADR-0016 | Dimension | phux | zmx | cmux | rmux | |---|---|---|---|---| -| Agent SDK or programmatic API | ✓ (planned L1 SDK) | ✗ | ✓ (macOS native) | ✓ (Rust async) | +| Agent SDK or programmatic API | ✓ (CLI + MCP shipped; Rust SDK planned) | ✗ | ✓ (macOS native) | ✓ (Rust async) | | Primary use case | Human multiplexer + control plane | Minimal session persistence | Agent UI (git, PR, notifications) | Agent automation | | Cross-platform | ✓ | ✗ (implied) | ✗ (macOS only) | ✓ | | Maturity | Pre-release v0.1 | Minimal scope, SSH bugs | Production (20k+ stars) | Public preview (v0.3.1) | @@ -120,7 +120,7 @@ phux is both a human multiplexer and a federation-ready control plane. See [ADR- Consumers in scope: - **Reference TUI.** Tmux-shaped: sessions, windows, panes, splits, status bar, keybindings. Speaks L1 + L2 + L3. See [`consumers/tui.md`](./consumers/tui.md). -- **Agent SDK** (`phux-client-sdk`, planned). Typed Rust handle to spawn, observe, drive Terminals. L1 only. +- **Agent surface (shipped).** The headless CLI verbs (`ls`, `snapshot`, `send-keys`, `run`, `wait`, `watch`, …) and the [`phux-mcp`](./consumers/mcp.md) adapter, both over the same selector grammar and JSON shapes. L1-shaped: spawn a terminal, drive it, read events and exit codes. A typed Rust SDK crate (`phux-client-sdk`) is still planned. - **Future:** native GUI, recorder, tmux control-mode adapter ([ADR-0010](../ADR/0010-frontend-agnostic-tmux-cc-reserved.md)). [ADR-0017 (TUI not protocol-privileged)](../ADR/0017-tui-not-protocol-privileged.md): the reference TUI is one consumer with no protocol-level privileges. If the TUI needs a feature the wire doesn't provide, extend the spec (with an ADR), not add a TUI-shaped hook. @@ -146,7 +146,7 @@ The TUI's vocabulary is user-facing. The substrate's vocabulary is what the wire - **L2 is stable.** Collection lifecycle, membership, naming, kill semantics. - **L3 exists as opaque storage.** Read / write / delete on metadata blobs. The TUI's conventions live in [`consumers/tui.md`](./consumers/tui.md) and the non-normative conventions appendix in [`spec/L3.md`](./spec/L3.md). - **The reference TUI works** on L1 + L3 metadata: attach, detach, splits, layouts, status bar, keybindings. -- **The agent SDK ships** as a thin L1-only wrapper, with examples that spawn a build, wait for OSC 133 command-end, read exit code. This is what makes the agent-first thesis real instead of aspirational. +- **The agent surface ships** — not as a Rust SDK crate yet, but as the headless CLI verbs and the `phux-mcp` adapter. A program can spawn a build, wait for it to settle or for output to appear, and read the exit code today. That's what makes the agent-first thesis real instead of aspirational; the typed `phux-client-sdk` crate is the convenience layer still to come. Federation and Automation are **designed for** in v0.1 (their hooks in the wire are present; their ADRs are written) and **shipped** in v0.2. Building for now, designed for later. diff --git a/docs/INSTALL.md b/docs/INSTALL.md new file mode 100644 index 0000000..2c4f4b6 --- /dev/null +++ b/docs/INSTALL.md @@ -0,0 +1,91 @@ +--- +audience: humans, contributors +stability: stable +last-reviewed: 2026-06-03 +--- + +# Install + +**TL;DR.** Homebrew on macOS and Linux x86_64 is the one-liner. From source is +a Nix dev shell plus one `cargo run` — that's the path if you're hacking on it +or you're on a platform the bottle doesn't cover. There is no `cargo install +phux` yet (only `phux-protocol` is published); the binary ships via brew and +source for now. + +--- + +## Homebrew (the easy one) + +macOS and Linux, x86_64: + +```sh +brew install phall1/phux/phux +phux # attached. that's it. +``` + +`phux` with no arguments auto-spawns a server and attaches to it. Detach with +`Ctrl-A d`; run `phux` again to come back. + +Apple Silicon and Linux aarch64 build fine from source today; a bottle for them +is on the list, not in the tap yet. + +## From source + +You need the toolchain phux is pinned to. The supported way to get it is the Nix +dev shell, because it pins everything — including the Zig compiler libghostty's +build wants — to versions known to work. + +```sh +git clone https://github.com/phall1/phux +cd phux +nix develop # or `direnv allow` once, then it loads on cd +cargo run --bin phux # auto-spawns a server and attaches +``` + +That's a working phux. For the guided first session — splits, config, +detach/reattach — see [`QUICKSTART.md`](./QUICKSTART.md). + +### Off Nix + +If you'd rather not use Nix, you're signing up to match the pins by hand. As of +this writing: + +- Rust **1.90** +- Zig **0.15** (`zig_0_15` — libghostty-vt's build invokes it) +- `cargo-nextest`, `cargo-deny` on `PATH` if you want to run the gates + +The Nix flake (`flake.nix`) is the source of truth for exact versions; when in +doubt, read it rather than this paragraph. + +### Verify the build + +```sh +just check # quick type-check across the workspace +just ci # the full bar: fmt-check + lint + test + deny + doc +``` + +`just ci` is what CI runs and what a PR has to pass. If it's green, you're good. + +## Drive it from an agent + +The agent surface ships with the same binary — nothing extra to install. The +MCP adapter is its own binary in the workspace: + +```sh +cargo run --bin phux-mcp # JSON-RPC over stdio; wire it into your MCP client +``` + +Tool catalog and JSON contracts: [`consumers/mcp.md`](./consumers/mcp.md). The +plain-CLI version of the same surface: [`consumers/agents.md`](./consumers/agents.md). + +## Platform support, honestly + +| Platform | Status | +|---|---| +| macOS (Apple Silicon) | Source: yes. Bottle: not yet. | +| macOS (x86_64) | Brew + source | +| Linux x86_64 | Brew + source | +| Linux aarch64 | Source: yes. Bottle: not yet. | +| Windows | No. Not on the near roadmap. | + +phux is v0.1. The install story gets shorter from here, not longer. diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index 2955c27..6edd5ee 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -1,16 +1,18 @@ --- audience: humans, contributors stability: stable -last-reviewed: 2026-05-28 +last-reviewed: 2026-06-03 --- # Quickstart **TL;DR.** Drop into the Nix-pinned dev shell, run `just ci` to verify the toolchain, then `cargo run` to spawn a server and attach. phux is -pre-alpha — what works today is single-pane attach with multi-pane -splits, keybindings, status bar, and config loading. Most lifecycle -and federation surface is not yet wired. +v0.1 — what works today is the TUI (attach/detach, multi-pane splits, +keybindings, status bar, config) plus the headless verbs you can script +or point an agent at (`ls`, `run`, `wait`, `watch`, `send-keys`, +`snapshot`, …). Federation routing is the main thing still on the wire +but not yet wired. --- @@ -65,15 +67,29 @@ right where you left it. - **Multi-client attach** to the same session. - **Bytes-on-wire terminal content**, structured input — full Kitty keyboard, OSC 8, OSC 133, true colour, image protocols pass through. +- **Headless verbs** you can run without a TTY: `ls`, `snapshot`, + `send-keys`, `run`, `wait`, `watch`, `new`, `kill`, `rename`, + `config`. Each addresses panes by the same selector grammar the TUI + uses; reads take `--json`. This is the surface a script — or an agent + — drives. See [`consumers/agents.md`](./consumers/agents.md). +- **MCP adapter** (`phux-mcp`): the same six core verbs as JSON-RPC + tools. See [`consumers/mcp.md`](./consumers/mcp.md). + +Try the headless side once you have a session up: + +```sh +phux ls --json # list sessions +phux run . "echo hello && exit 3" # run in the focused pane, get exit code 3 back +phux watch --json . # stream live events; Ctrl-C to stop +``` ## What doesn't yet -- The full subcommand set (`phux new`, `phux ls`, `phux kill`) — only - `phux` (naked, auto-attach) and `phux server` ship today. -- Most L2 Collection lifecycle and L3 metadata commands. -- Federation routing (satellites, hubs). -- The agent SDK. -- Predictive local echo (designed for; gated on a transport whose +- **Federation routing** (satellites, hubs). The wire already accepts + `SATELLITE{host, id}`; nothing routes it. v0.2. +- **The typed Rust SDK crate** (`phux-client-sdk`) — the CLI and MCP + surfaces cover agent use today; the crate is convenience on top. +- **Predictive local echo** (designed for; gated on a transport whose RTT actually needs it). Each of these is spec'd before it's built, so the wire hooks are diff --git a/docs/README.md b/docs/README.md index 6d70ea8..ab54217 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,42 +1,55 @@ --- audience: contributors, agents stability: stable -last-reviewed: 2026-05-28 +last-reviewed: 2026-06-03 --- # docs/ -**TL;DR.** The doc tree. Concepts and consumer-shaped guides live as -single files; the wire spec and the architecture description are split -per concept under their own subdirectories. - -**Start here:** [`CONVENTIONS.md`](./CONVENTIONS.md) defines the doc -system itself (frontmatter, TL;DR rule, ADR template, CI gates) — read -before adding or moving content. +**TL;DR.** The doc tree, and the order to read it in. Concepts and +consumer guides are single files; the normative wire spec and the +architecture description are split per concept under their own +subdirectories. New here? [`CONCEPTS.md`](./CONCEPTS.md) first, then pick +a lane below. Adding or moving docs? [`CONVENTIONS.md`](./CONVENTIONS.md) +first — it's the law (frontmatter, TL;DR rule, ADR template, CI gates). --- -## Layout +## Read in this order + +If you're just trying to understand phux, top to bottom: + +1. [`CONCEPTS.md`](./CONCEPTS.md) — what phux actually is. The terminal + as the unit; human and agent as peer consumers; the layered wire. + Read this even if you read nothing else. +2. [`QUICKSTART.md`](./QUICKSTART.md) — get a session running and poke at it. +3. [`vision.md`](./vision.md) — where it's headed, and why the v0.1 wire + is already shaped for it. + +## Pick a lane -| Path | Owns | +| You are | Go to | |---|---| -| [CONVENTIONS.md](./CONVENTIONS.md) | The doc system itself: layers, frontmatter, TL;DR rule, ADR template, CI gates | -| [CONCEPTS.md](./CONCEPTS.md) | The canonical "what is phux" — terminals as substrate, layered protocol, libghostty foundation, federation arc | -| [QUICKSTART.md](./QUICKSTART.md) | Run the dev shell, attach a terminal, exercise the demo | -| [vision.md](./vision.md) | The long arc — agents, federation, the "smol" thesis | -| [operations.md](./operations.md) | Errors, logging, telemetry, security boundaries | -| [spec/](./spec/) | Normative wire — proto / L1 / L2 / L3 / appendices, versioned with `phux-protocol` | -| [architecture/](./architecture/) | Process model, threading, transport, crate graph, state replay | -| [consumers/](./consumers/) | One file per consumer surface — `tui.md`, `sdk.md` | +| Installing it | [`INSTALL.md`](./INSTALL.md) | +| Running it for the first time | [`QUICKSTART.md`](./QUICKSTART.md) | +| Configuring keys / status bar | [`CONFIG.md`](./CONFIG.md) | +| Driving it from an agent | [`consumers/agents.md`](./consumers/agents.md) (CLI) · [`consumers/mcp.md`](./consumers/mcp.md) (MCP) | +| Writing a different consumer | [`consumers/tui.md`](./consumers/tui.md) · [`consumers/web.md`](./consumers/web.md) | +| Implementing against the wire | [`spec/`](./spec/) — normative, versioned with `phux-protocol` | +| Reading how it's built | [`architecture/`](./architecture/) — process model, threading, transport, crate graph | +| Operating it | [`operations.md`](./operations.md) — errors, logging, telemetry, security boundaries | +| Understanding a past decision | [`../ADR/README.md`](../ADR/README.md) | +| Touching the docs themselves | [`CONVENTIONS.md`](./CONVENTIONS.md) | [`../ADR/`](../ADR/) holds decision records — one decision per file, Nygard template, strict `Status:` vocabulary. Start at [`../ADR/README.md`](../ADR/README.md) for the index. -## What's not here +## What's deliberately not here -- Code-level documentation lives in `crates/*/src/` as rustdoc. - `cargo doc --workspace --all-features` is the rendered view. -- Scratch research lives in [`../research/`](../research/) with - `stability: scratch`. Ratified findings move into ADRs or one of the - reference subdirs above. +- **Code-level docs** live in `crates/*/src/` as rustdoc. + `cargo doc --workspace --all-features` renders them. +- **Scratch research** lives in [`../research/`](../research/) at + `stability: scratch`. Once a finding is ratified it graduates into an + ADR or one of the reference docs above — it doesn't linger here as + half-truth. diff --git a/docs/vision.md b/docs/vision.md index 5907bbd..398e1f4 100644 --- a/docs/vision.md +++ b/docs/vision.md @@ -1,7 +1,7 @@ --- audience: humans, contributors, agents stability: evolving -last-reviewed: 2026-05-28 +last-reviewed: 2026-06-03 --- # Vision @@ -14,6 +14,11 @@ today — read [`CONCEPTS.md`](./CONCEPTS.md). --- +This is the part where we tell you where it's all going. Standard +caveat applies: it's a direction, not a delivery date. The reason to +write it down is that the v0.1 wire was shaped by it — the forward +compatibility below isn't a someday-maybe, it's already in the bytes. + ## Why now Two structural changes, neither of which existed when tmux's @@ -100,18 +105,21 @@ chrome the TUI grows over its layered substrate. ### The agent SDK -A small Rust crate (`phux-client-sdk`) giving a program a typed -handle to spawn, observe, and drive Terminals over the wire. L1 only. -No sessions, no windows, no layout. The agent's universe is -*terminals and events*: spawn a build, wait for the OSC 133 -command-end event, read the exit code, kill the terminal, move on. +The agent's universe is *terminals and events*: spawn a build, wait +for the OSC 133 command-end event, read the exit code, kill the +terminal, move on. Two ways to reach it ship today — the headless +`phux` CLI verbs (`run`, `wait`, `watch`, `send-keys`, `snapshot`, …) +and the [`phux-mcp`](./consumers/mcp.md) adapter, both L1-shaped with no +sessions, windows, or layout in sight. -A future `phux` CLI grows the same primitives for shell use — `phux -spawn`, `phux observe`, `phux exec`. JSON-over-HTTP shows up if -non-Rust agents become a real consumer category. +What's still on the arc here is the *ergonomic* layer: a small Rust +crate (`phux-client-sdk`) giving a program a typed handle over the same +wire, and JSON-over-HTTP if non-Rust agents that can't speak MCP become +a real consumer category. The surface that matters already exists; this +is convenience on top of it. -[`consumers/sdk.md`](./consumers/sdk.md) is the surface doc (currently -a stub). +[`consumers/agents.md`](./consumers/agents.md) and +[`consumers/mcp.md`](./consumers/mcp.md) are the shipped-surface docs. --- @@ -119,8 +127,9 @@ a stub). - **v0.1 — substrate cut.** L1 frozen, L2 stable, L3 as opaque storage. Reference TUI works on L1 + L3 for layout. Federation hooks - are baked into the wire but not exercised. Agent SDK ships as an - L1-only wrapper. + are baked into the wire but not exercised. The agent surface ships as + the headless CLI verbs plus the `phux-mcp` adapter; the typed Rust SDK + crate is the convenience layer still to come. - **v0.2 — federation real.** Hubs route to satellites. QUIC transport. Lazy state sync replaces pass-through bytes per ADR-0018. - **v0.x and beyond — second consumer.** A native GUI consumer From 82b31ae4643fd40561f0b55106fbef7a5c4698ad Mon Sep 17 00:00:00 2001 From: phall1 Date: Wed, 3 Jun 2026 03:48:43 -0400 Subject: [PATCH 2/3] docs: add demo storyboard, wire README placeholder, fix MCP-verb wording The demo is the highest-leverage missing asset; give it a turnkey recipe instead of a bare TODO. docs/demo.md is the two-beat storyboard (content survives reattach; same thing driven headless), the reproducible payload, recording tools, and where the GIF lands. README placeholder now points at it. Also drop the imprecise "same six verbs" line (MCP tools and the CLI block don't line up one-to-one). Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 10 +++--- docs/README.md | 1 + docs/demo.md | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 docs/demo.md diff --git a/README.md b/README.md index 18b0c7b..121c204 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,9 @@ That second one is doing more than it lets on. We'll get to it. + most important pixel on the page. Recipe: docs/demo.md. + When the GIF lands at docs/assets/demo.gif, replace this comment with: + ![phux: modern terminal content surviving a detach/reattach, then driven headless](docs/assets/demo.gif) --> ## Try it @@ -56,9 +58,9 @@ phux wait build --until "0 failed" # blocks until the output shows up, exit phux watch --json work:1.0 | jq . # live events: bells, titles, idle, lifecycle ``` -Point an MCP-speaking agent at it instead and it gets the same six verbs as -tools (`phux-mcp`, JSON-RPC over stdio). The agent isn't peeking at a human's -session through a keyhole. It's a first-class user with the same keys. +Point an MCP-speaking agent at it instead and it gets the same surface as tools +— `phux-mcp` exposes the core verbs over JSON-RPC stdio. The agent isn't peeking +at a human's session through a keyhole. It's a first-class user with the same keys. ## Why it's built this way diff --git a/docs/README.md b/docs/README.md index ab54217..639d010 100644 --- a/docs/README.md +++ b/docs/README.md @@ -39,6 +39,7 @@ If you're just trying to understand phux, top to bottom: | Reading how it's built | [`architecture/`](./architecture/) — process model, threading, transport, crate graph | | Operating it | [`operations.md`](./operations.md) — errors, logging, telemetry, security boundaries | | Understanding a past decision | [`../ADR/README.md`](../ADR/README.md) | +| Recording the README demo | [`demo.md`](./demo.md) | | Touching the docs themselves | [`CONVENTIONS.md`](./CONVENTIONS.md) | [`../ADR/`](../ADR/) holds decision records — one decision per file, diff --git a/docs/demo.md b/docs/demo.md new file mode 100644 index 0000000..d5d3268 --- /dev/null +++ b/docs/demo.md @@ -0,0 +1,87 @@ +--- +audience: contributors +stability: stable +last-reviewed: 2026-06-03 +--- + +# Recording the README demo + +**TL;DR.** The README has a demo-shaped hole at the top and it's the most +important pixel on the page. This is the storyboard for filling it: two short +beats — *modern terminal content survives a detach/reattach*, then *the same +thing driven headless* — recorded as a GIF and dropped at +`docs/assets/demo.gif`. Use a real screen-recorder for the first beat +(asciinema players don't render kitty graphics, which is the whole point); +asciinema is fine for the second. + +--- + +## The two beats + +Keep it under ~15 seconds. The pitch is "look how little is going on, and yet." + +**Beat 1 — it survives (≈8s).** In a graphics-capable terminal (Ghostty, +kitty, WezTerm), run `phux`, paint something a lesser multiplexer would mangle, +detach, reattach. The viewer should see the fancy content come back *intact*. + +```sh +phux # attach +# in the pane: paint a truecolor gradient + an inline image +bash docs/assets/payload.sh # (see "the payload" below) — or any kitty +kitten icat IMAGE +# detach: +Ctrl-A d +# reattach — the gradient and the image are still there, pixel-for-pixel: +phux +``` + +**Beat 2 — an agent could've done that (≈6s).** Drop to a second terminal and +drive the *same* session without a TTY. This is the line that makes a skimmer +stop. + +```sh +phux run . "cargo test --quiet" # runs in the live pane, prints the exit code +phux watch --json . | jq . # live events scrolling by; Ctrl-C to cut +``` + +Cut on the JSON events scrolling. No narration, no captions. Let the +juxtaposition do it. + +## The payload + +For Beat 1 you want something visibly *modern* so the "survives reattach" claim +is legible. A 256-step truecolor gradient is the cheapest unmistakable one: + +```sh +# truecolor sweep — every column a distinct 24-bit color +awk 'BEGIN{for(i=0;i<256;i++)printf "\033[48;2;%d;%d;%dm ",i,(128+i)%256,255-i;print "\033[0m"}' +``` + +For the real flex, add an inline image with your terminal's image kitten +(`kitty +kitten icat logo.png`, or Ghostty/WezTerm's equivalent). The image is +what tmux literally cannot carry across a reattach; the gradient is what it +carries badly. + +Park whatever you settle on at `docs/assets/payload.sh` so the recording is +reproducible and the next person doesn't reinvent it. + +## Tools + +- **GIF (Beat 1, required):** any screen-recorder that captures real pixels — + [`vhs`](https://github.com/charmbracelet/vhs) if you want it scripted and + deterministic, or a plain screen-capture-to-GIF. Kitty-graphics frames must + survive into the GIF, so this can't be a terminal-only recorder. +- **asciinema (Beat 2, optional):** fine for the headless half if you'd rather + splice. [`agg`](https://github.com/asciinema/agg) renders a `.cast` to GIF. +- Target **≤ 2 MB** so it loads before the reader scrolls past it. Trim + generously; the demo is a hook, not a tutorial. + +## Wiring it in + +Drop the finished file at `docs/assets/demo.gif`, then replace the +`` comment at the top of the root `README.md` with: + +```markdown +![phux: modern terminal content surviving a detach/reattach, then driven headless](docs/assets/demo.gif) +``` + +That's the page going from half-built to built. From 5720d1b38df48fb7ebfc213526502fbbc0fbcd01 Mon Sep 17 00:00:00 2001 From: phall1 Date: Wed, 3 Jun 2026 03:49:59 -0400 Subject: [PATCH 3/3] =?UTF-8?q?docs:=20fix=20docs-check=20dead-links=20?= =?UTF-8?q?=E2=80=94=20don't=20write=20the=20demo.gif=20image=20syntax=20u?= =?UTF-8?q?ntil=20it=20exists?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CI dead-link checker resolves relative links inside comments and code fences too, so the placeholder image markdown pointed at a file that won't exist until the GIF is recorded. Reference the path as prose instead. Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 6 +++--- docs/demo.md | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 121c204..48fe4b1 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,9 @@ That second one is doing more than it lets on. We'll get to it. + most important pixel on the page. Recipe and storyboard: docs/demo.md. + When the GIF lands at docs/assets/demo.gif, replace this whole comment + with a standard markdown image pointing at that path. --> ## Try it diff --git a/docs/demo.md b/docs/demo.md index d5d3268..0c26480 100644 --- a/docs/demo.md +++ b/docs/demo.md @@ -78,10 +78,12 @@ reproducible and the next person doesn't reinvent it. ## Wiring it in Drop the finished file at `docs/assets/demo.gif`, then replace the -`` comment at the top of the root `README.md` with: +`` comment at the top of the root `README.md` with a +standard markdown image: `!` then `[alt text]` then `(docs/assets/demo.gif)`, +where the alt text is something like *phux: modern terminal content surviving a +detach/reattach, then driven headless*. -```markdown -![phux: modern terminal content surviving a detach/reattach, then driven headless](docs/assets/demo.gif) -``` +(The literal image syntax isn't shown inline here on purpose — the docs CI +resolves every relative link, and the GIF doesn't exist until you record it.) That's the page going from half-built to built.