l0ng-ai bf5149bea0 fix(editor): stop losing edits, share buffers, add file strip, quick open and go to line (#984)
* fix(host): save local files atomically via a temp file and rename

LocalHost::write_file truncated the target in place, so a crash, a full
disk or a killed process mid-save destroyed the user's file. It now
writes a hidden sibling temp file, syncs it, keeps the old file's mode
and renames it over the target, removing the temp file on any error.

It still writes in place where a rename would change something visible:
a non-regular target (symlink, directory, FIFO), a read-only file, and on
Unix a hard-linked file or one owned by another user, or when the temp
file cannot be created (e.g. a read-only directory).

* feat(editor): add editor_text for encodings, line endings, indentation and EditorConfig

A pure module the code editor will use when loading and saving files:
decode detects BOMs, binary files, UTF-8, GB18030 and a lossless
Windows-1252 fallback and normalises CRLF; encode restores the exact
bytes and names the first unrepresentable character; detect_indent
infers tabs or a 2/4/8 space width with language defaults; and
editorconfig_for resolves .editorconfig sections with save-time rules.

* feat(editor): share buffers across tabs, guard unsaved work, add a file strip

- One buffer per file per window; tabs list which buffers they show. The
  same file open in two tabs is no longer two diverging copies.
- Closing a tab, its last pane, the window, or quitting asks about unsaved
  files (Save / Cancel / Discard) instead of dropping them. Bulk closes skip
  tabs with unsaved files; a tab that vanishes any other way hands its
  unsaved buffers to the tab in front.
- File tree rename/delete now retarget or flag the open buffer, so a save
  no longer recreates the old path.
- Saves check the file's mtime first and ask before overwriting a change
  made elsewhere; this is the only detection SFTP buffers get.
- Dirty is a comparison with the saved text, so undoing back clears it.
- Reloads replace only the changed span as an ordinary edit, keeping undo.
- Load/save go through editor_text: encoding, BOM and CRLF round-trip,
  indentation is detected, .editorconfig is honoured.
- Header shows a strip of open files; New File, Save As (native panel
  locally, a path bar remotely), Go to Line (Ctrl+G), and the status bar
  shows indentation, encoding and a clickable line ending.
- Open files are remembered per tab across restarts.

* feat(search): quick open a file by name from a Files tab

Search Everywhere gains a Files tab that finds any file in the active
tab's project by fuzzy name and opens it in the built-in editor, with
`name:line[:col]` jumping to that spot. The list comes from one walk of
the project through the host (Host::search with an empty query), so it
works the same on local, SSH and WSL workspaces and skips what the tree
hides: dotfiles, .git and gitignored paths. The walk is capped at 50k
entries / 20k directories, kept between openings and revalidated in the
background each time the search opens.

Files join the All tab once a query finds them. Go to File... is bound to
Cmd+O on macOS (Cmd+P is already Search Everywhere) and ships unbound
elsewhere, where every obvious chord is taken or owed to the shell.

* chore(editor): allowlist the editor session file, tidy lints

* fix(editor): keep restored file order, drop stale close waits, carry files through tab merges

- Background arrivals (restore, merge, rescue) append to the strip in order
  instead of inserting beside the active file, which reversed them.
- A cancelled Save As, a dismissed path bar, or a dropped buffer cancels any
  close that was waiting on that save.
- Merging a tab into another carries its open files along.
- A shell exiting closes its tab without a prompt it could not honour;
  unsaved buffers move to the tab in front.
- Tabs rebuilt under the same id (server restart) restore their files.

* fix(host): only fall back to an in-place write when the rename is refused

On Windows every failure of the atomic save fell back to fs::write,
including a failure while staging the temp file. A full disk would then
truncate the original in place, the very loss the temp file prevents.
Staging errors now return as-is; only a refused rename (a file held open
elsewhere) takes the in-place path.
2026-09-28 00:32:18 +08:00

tty7

tty7

A terminal workbench: persistent sessions, remote work, agents.

Pure Rust · GPU rendering on Zed's gpui · VT core from Alacritty


CI Version Platforms License Discord

English · 简体中文


One-minute tour of tty7: agents across repos, one agent driving another through the CLI, the prompt editor, diffs, dragging panes, and sessions that survive quitting the app

▶ Watch in full quality

Why

A background server owns your shells and panes — not the window. Everything below follows from that.

  • Performance — ~2× the throughput of Alacritty, Ghostty, or Kitty (benchmarks)
  • Persistent sessions — quit or reboot; your shells and supported agent sessions keep running, no tmux
  • Agent-aware — Claude Code, Codex & co.: status, notifications, and git context for every repo at once
  • Scriptable by agents — one agent opens a pane for another, hands off a task, waits, and reads the result, with or without the GUI running
  • Editor-grade input — suggestions, completion, highlighting, history search, with no plugin to install
  • Remote development — files, repos, panes, and git data stay on the remote machine, over a native SSH stack

Install

Native builds for macOS, Windows, and Linux on Releases:

macOS …-macos-arm64.dmg · …-x86_64.dmg drag into Applications
Windows …-setup.exe · portable ….zip
Linux …-x86_64.AppImage chmod +x and run — X11/Wayland libraries bundled

What's inside

Agent-aware per-pane detection (25 CLIs) · status dot · notifications · branch + diff · tray icon when input is needed · resume after reboot · tab sidebar grouped by repository, with pinned groups
CLI + Skills bundled tty7 CLI · agent skill · run streams a command and exits with its code · split · send · wait --until free · capture
Editor-grade input ghost suggestions from history · explained tab completion · syntax highlighting · multi-line editing · click places the caret · ⌃ R fuzzy history
Window tabs & splits · ⌘ P Search Everywhere · ⌘ F scrollback search · ⌘ J panel with process tree and listening ports · 13 themes, your own YAML, iTerm2 import · IME
Shell integration injected when a pane starts, nothing to install · prompt marks · working directory · exit codes · command-finished notifications · zsh, bash, fish, PowerShell, WSL, remote panes
Remote workspaces remote files, repos, changes, diffs, worktrees, tabs, and panes · reconnect from any client and continue where you left off
SSH native russh stack: profiles with keychain secrets · SFTP panel · port forwarding · jump hosts · one-time, unprivileged tty7-server install

Supported agents

Detection is free: brand avatar, branch + diff, tab title. Status takes one click under Settings → Integrations to install that agent's hook, and brings the status dot, notifications, the tray icon, tty7 wait, and resume after a reboot. Fork needs both — the agent's own fork command, and the hook that tells tty7 which session to fork.

The full support matrix, all twenty-five
Agent Detected Status · resume Fork
Claude Code ✓ ✓ ✓
Codex ✓ ✓ ✓
TraeCode ✓ ✓ ✓
Grok ✓ ✓ ✓
OpenCode ✓ ✓ ✓
Oh My Pi ✓ ✓ ✓
Prime Agent ✓ ✓ ✓
Droid ✓ ✓ ✓
Qwen Code ✓ ✓ ✓
Goose ✓ ✓ ✓
Qoder CLI ✓ ✓ ✓
CodeBuddy ✓ ✓ ✓
Gemini ✓ ✓
Copilot ✓ ✓
Kimi Code ✓ ✓
Pi ✓ ✓
Crush ✓ ✓
Antigravity ✓ ✓
Cursor ✓ ✓
Aider ✓
Amp ✓
Auggie ✓
Hermes ✓
Vibe ✓
Empryo ✓

None of them are wrapped or proxied — the agent you start is the agent you get, in a normal PTY, with its own interface. An agent launched through a wrapper script can be mapped to one by name with agent_commands in config.json. Every agent on your PATH is also a row in Search Everywhere (Agent: Claude Code, …) that opens it in a new tab; agent_launch sets the command line it starts with.

Documentation

Full documentation lives in docs/ — keyboard shortcuts · config.json · CLI reference. The agent-facing CLI interface is also documented in skills/tty7/SKILL.md.

Install the skill with:

npx skills add l0ng-ai/tty7    # install
npx skills update tty7         # update later

Benchmarks

Same machine, same day, same 155×40 grid — Apple M1 Pro, macOS 26.3.1, five-run averages (2026-07-04):

tty7 Alacritty Ghostty Kitty
Plaintext I/O — 11 MB cat (lower = better) 95 ms 239 ms 179 ms 185 ms
DOOM-fire frame rate (higher = better) 888 fps 485 fps 552 fps 617 fps
Cold-launch memory 116 MB¹ 105 MB 128 MB 130 MB

¹ GUI 105 MB + the persistent server 11 MB.

Methodology and one-command reproduction: scripts/bench/.


S
Description
A terminal workbench in pure Rust: shells, persistent sessions, SSH, coding agents. GPU-rendered on Zed's gpui, VT core from Alacritty.
Readme Apache-2.0
43 MiB
Languages
Rust 97.8%
TypeScript 1%
Shell 0.5%
CSS 0.3%
PowerShell 0.2%
Other 0.1%