diff --git a/README.md b/README.md index b247bd7..48fe4b1 100644 --- a/README.md +++ b/README.md @@ -6,170 +6,156 @@ 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 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. -## 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..639d010 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,42 +1,56 @@ --- 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) | +| 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, 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/demo.md b/docs/demo.md new file mode 100644 index 0000000..0c26480 --- /dev/null +++ b/docs/demo.md @@ -0,0 +1,89 @@ +--- +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 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*. + +(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. 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