Daemon-owned terminal multiplexer with block-level history, full-text search, and agent observability.
- Block model (OSC 133) — every command is a first-class
Block: command string, cwd, stdout, exit code, timestamps. OSC 133 markers feedBlockParser→BlobWriter→ SQLite + Tantivy.pyrec listandpyrec searchquery the index. - Persistent sessions —
pyredowns every PTY; client crashes and SSH drops do not kill the session. SQLite store survives daemon restart and the supervisor reattaches workers on init. - Multi-client mirror — N TUIs can attach to the same pane simultaneously. Output is broadcast to all attached clients; input is serialized through the daemon.
- Reattach with replay — restarting a
pyreTUI restores the grid snapshot from the per-pane ring buffer and replays the last 20 blocks via thereplayRPC. - Hybrid daemon (ADR-002) — selectable process model: a single
pyred(default for v0.1.0) or a thin supervisor onpyre.sockplus per-session worker processes onpyre/session-<id>.sock. Worker crash kills only its session. - Full-text search — Tantivy indexes all block output;
pyrec search <query>returns ranked hits with snippets in milliseconds. - Agent state monitoring — a dedicated state tracker classifies each pane as idle, running, waiting for input, or error; exposed via the MCP server.
- MCP server (
pyre-mcp) — seven tools:session_spawn,session_close,pane_open,pane_send_keys,pane_capture,pane_set_state,block_search. Sessions, panes, and blocks are also exposed as MCP resources. - Mouse-first TUI — ratatui + crossterm, mouse click-to-focus, scroll wheel. Ships with 18 built-in palettes via the
pyre-themesregistry; switch live withCtrl-Space T. - Pyre fire motion — startup splash and in-TUI accents use a shared procedural fire engine (
fire_motion.rs): no animation libraries, no sprite packs. Blocked agents pulse like embers on the same palette as the launch animation. - tmux-compatible CLI —
pyrecacceptslist-sessions,new-session,kill-session,send-keys,split-window, and more. - Clipboard integration —
pyrec capture-pane --copycopies output to the system clipboard viawl-copy(Wayland) orxclip(X11). - Searchable scrollback — ring buffer per pane with configurable depth;
PgUp/PgDninpyreorcapture-pane -Sinpyrec.
git clone https://github.com/Rixmerz/pyre.git
cd pyre
cargo build --release
# binaries: target/release/{pyred,pyrec,pyre,pyre-gpu,pyre-mcp}For a size- and performance-optimised binary use the release-prod profile:
cargo build --profile release-prod --workspacesudo install -Dm755 target/release/pyred /usr/bin/pyred
sudo install -Dm644 dist/systemd/pyred.service /usr/lib/systemd/user/pyred.service
systemctl --user daemon-reload
systemctl --user enable --now pyredTo opt into the hybrid supervisor/worker model, set process_model = "hybrid"
in your pyred config (see docs/CONFIG.md). Default is
"single" for v0.1.0; see docs/adr/0002-daemon-process-architecture.md.
# Open pyre (recommended for interactive use)
pyre
# Or use pyrec directly
pyrec # spawn + attach default shell
pyrec sessions # list active sessions
pyrec list # list recent blocksWith process_model = "hybrid" in config (see docs/CONFIG.md):
pyrec session-new --name api --cwd ~/projects/api -d
pyrec session-new --name web --cwd ~/projects/web -d
pyre # sidebar shows per-pane blocked/working + session rollupFrom scripts or an MCP client: pyrec wait-pane --pane <id> --state waiting,
pyrec pane read --pane <id> --source block-last. Playbook:
docs/AGENTS.md.
pyre-gpu # windowed attach; Ctrl+/ search; Ctrl+Tab switch panes
pyrec doctorSee docs/adr/0003-render-backend.md.
Prefix: Ctrl-Space
| Keys | Action |
|---|---|
Ctrl-Space q |
Quit / detach |
Ctrl-Space c |
New pane in current session |
Ctrl-Space x |
Close current pane |
Ctrl-Space n |
Next tab |
Ctrl-Space p |
Previous tab |
Ctrl-Space " |
Horizontal split |
Ctrl-Space % |
Vertical split |
| Arrow keys | Move focus between panes |
Ctrl-Space [ |
Enter scrollback mode |
Ctrl-Space ] |
Exit scrollback mode |
Ctrl-Space / |
Search blocks (Tantivy query dialog) |
Ctrl-Space z |
Zoom (toggle fullscreen) current pane |
Ctrl-Space y |
Copy last block stdout to clipboard |
Ctrl-Space s |
Toggle sidebar |
Ctrl-Space S |
New session |
Ctrl-Space T |
Open theme picker overlay (live switch across 18 palettes) |
Ctrl-Space N |
Toggle toast notifications on/off |
PgUp / PgDn |
Scroll in scrollback mode |
| Mouse click | Focus pane under cursor |
| Mouse wheel | Scroll output |
Exercises daemon, TUI, pyrec, and search end-to-end on a single machine.
Assumes binaries are built and pyred is not already running.
pyred &— start the daemon in single-process mode (default). Usepyred --config <path>withprocess_model = "hybrid"for the supervisor model.pyre— launch the TUI; a fresh session opens automatically (the daemon spawns a default pane).Ctrl-Space "— horizontal split; a second pane appears below the first.- Type
echo hello && sleep 1 && echo donein the lower pane and press Enter; wait for the block to finish (exit badgeb<id>appears in the ribbon). Ctrl-Space [— enter block ribbon mode on the focused pane;←/→(orh/l) move the cursor between recent blocks.- Press
Enteron a block — the modal pager opens showing the full stdout.↑/↓orPgUp/PgDnscroll;qorEsccloses. - In a second terminal:
pyrec search "hello"— confirm the block is indexed (output includes a snippet line). - Back in the TUI:
Ctrl-Space /— open the search overlay; typehello;↑/↓navigate hits;Enterjumps focus to the source pane and highlights the block in the ribbon. Ctrl-Space y— copy the last block's stdout to the clipboard (requireswl-copyon Wayland orxclipon X11).pyrec kill-session <session-id>(UUID prefix accepted) — close the session cleanly;pyrec sessionsshould return an empty list.
Note: pyrec wait-pane --pane <pane-id> --state waiting can be used in step 5 instead of watching the TUI — it returns as soon as the pane transitions to WaitingInput (or the shell prompt reappears with OSC 133 integration installed).
# Spawn a new session with zsh
pyrec --shell /bin/zsh
# List sessions
pyrec sessions
# Attach to an existing session (UUID prefix accepted)
pyrec attach <session-id>
# Open a second pane in a session
pyrec new-pane --session <session-id>
# Send keys to a pane (tmux-compat)
pyrec send-keys --session <session-id> --pane <pane-id> -- "ls -la\n"
# Capture last 40 lines of a pane
pyrec capture-pane --session <session-id> --pane <pane-id> --lines 40
# Full-text search across all block stdout
pyrec search "cargo error"
# Kill a session
pyrec kill-session <session-id>pyre-tui and pyre-gpu consume the shared pyre-themes registry. 18 built-in
palettes ship today:
catppuccin-mocha, catppuccin-latte, tokyo-night, tokyo-night-light,
gruvbox-dark, gruvbox-light, one-dark, one-light, solarized-dark,
solarized-light, kanagawa, rose-pine, rose-pine-dawn, vesper, nord,
dracula, terminal, ember.
-
Open the live picker with
Ctrl-Space Tinpyre—↑/↓(orj/k) navigate,Enterapplies and persists,Esccancels. -
Set the startup default in
$XDG_CONFIG_HOME/pyre/config.toml:[ui] theme = "catppuccin-mocha"
-
pyre-gpureads the same[ui] themekey on startup; live switch lands in a later commit (restart required for now).
See docs/CONFIG.md for the full [ui] schema.
Pane lifecycle events (spawn, close, state change to WaitingInput / Done /
Crashed) surface as toast cards rendered bottom-right of the TUI. Idle and
running transitions are suppressed to avoid spam.
- Toggle on/off at runtime with
Ctrl-Space N. - Configure defaults under
[ui.notifications]inconfig.toml(see docs/CONFIG.md).
M2 of the v0.2 UX sprint is in flight: desktop bridge (notify-send / D-Bus
on Linux, osascript on macOS) and per-kind routing rules are pending. Today
the toast deck is in-TUI only.
pyred binds a Unix Domain Socket and does not listen on TCP. For laptop ↔
remote-server attach, pyrec remote <host> derives the local + remote socket
paths and prints (or with --exec runs) an ssh -L tunnel command. The
local pyre --socket <path> then attaches as if pyred were local — zero
daemon changes, SSH owns auth + transport.
pyrec remote alice@dev.box # prints the ssh -L command
pyrec remote alice@dev.box --exec # runs the tunnel in foreground
pyrec remote alice@dev.box --remote-socket /run/user/1000/pyre.sockThe wire-format decision (SSH tunnel for v0.2, native TLS for v0.3) is recorded
in docs/adr/0004-remote-attach.md (Proposed).
Implementation polish (auto-detection of XDG_RUNTIME_DIR on the remote,
keepalive defaults, reconnect) is pending.
See ARCHITECTURE.md for the full process diagram, crate map, block lifecycle, and data flow. The hybrid supervisor/worker layout is specified in docs/adr/0002-daemon-process-architecture.md.
| File | Contents |
|---|---|
| docs/USAGE.md | All subcommands, tmux mapping table, TUI bindings, troubleshooting |
| docs/CONFIG.md | hooks.toml schema, process_model flag, future config knobs |
| ARCHITECTURE.md | Crate map, process diagram, block lifecycle, state engine |
| SPEC.md | Full feature specification and IPC method reference |
| docs/adr/0002-daemon-process-architecture.md | Hybrid supervisor/worker decision |
| ROADMAP.md | Sprint table, MVP criterion, risks |
| CHANGELOG.md | Release history |
- Fork the repo and create a feature branch.
- Run
cargo fmt --all && cargo clippy --workspace --all-targets -- -D warningsbefore pushing. - All tests must pass:
cargo test --workspace. - Open a PR with a description of what changed and why.
Commit style: Conventional Commits with a Why: body line.
Dual-licensed under MIT or Apache-2.0 at your option.