5 Commits
Author SHA1 Message Date
l0ng-ai 0c9f4baa3a fix(cli): make the PATH install reversible, honest, and safe to migrate
Follow-up on the review of #277. Seven fixes, no change to what the feature
is for.

An AppImage copy is now claimed with a marker file instead of being inferred
from "am I an AppImage right now". Keying off the runtime meant that a user
who moved from the AppImage to the tarball hit their own copy, read it as
somebody else's binary, and never got another install for as long as that file
sat there.

The Windows uninstaller takes {app} back out of HKCU\Environment. Nothing did
before: the entry is written by the app at runtime, so Inno never knew it
existed and every uninstall grew the user's PATH by one dead entry. Unix has no
equivalent hook and still leaves its symlink behind; that is now stated in the
module docs rather than left to be discovered.

An occupied candidate directory no longer ends the scan, and every platform now
reports whether the install actually wins the lookup. `Occupied` on
/opt/homebrew/bin used to mean giving up while ~/.local/bin sat free, and
Windows — which appends to PATH and so never collides — reported `Installed`
even when an existing tty7 earlier on PATH kept beating it. A new
`InstalledShadowed` names the winner.

`cargo run --release` no longer repoints the developer's real tty7 at a build
tree. `cfg!(debug_assertions)` only covered the debug half of that.

The Windows registry PATH is read, matched, and written as UTF-16 throughout.
It went through `to_string_lossy` before, so a value the registry holds but
Rust cannot represent as a String would have been written back with U+FFFD in
place of its characters — the exact PATH corruption the surrounding code is
careful to avoid.

Two tests mutated $HOME and $PATH while the rest of the binary's tests ran
beside them, and src/ui/home.rs mutates $HOME too. `candidate_dirs` takes home
as a parameter, `place` takes its mode, and the PATH-joining and registry-
joining rules are pure functions — so no test in this module touches the
environment any more. 5 tests become 11, and the Windows joining logic is
covered on every platform.

Also: the config flag reaches Settings → About and both features docs instead
of being config.json-only, startup reads config.json once instead of twice, and
the CLI's strip failure warns like its sibling instead of being swallowed.
2026-07-31 16:49:22 +08:00
l0ng-ai c275960ceb feat(cli): ship the CLI in every installer and put it on PATH at launch
The `tty7` CLI was built by every release run and thrown away: all four
bundle scripts copied only `tty7-app`, and the upload glob covers `dist/`,
which the CLI never reached. Nothing put it on PATH either, so the
agent-facing half of the product was unreachable from a shipped install.

Bundle it on all four platforms, and have the GUI link it up itself rather
than hiding the step behind a menu item most people never find.

The install has two halves. The environment half prepends the CLI's
directory to this process's PATH before the daemon is spawned, so every
pane inherits it — that alone makes `tty7` work where agents actually run,
writes nothing to disk, and behaves the same everywhere. The on-disk half
symlinks into a directory already on PATH (Unix) or appends to
HKCU\Environment (Windows), and is allowed to fail.

Candidate directories are a fixed list intersected with PATH, not the first
writable entry on it: pyenv/rbenv/asdf/mise shim directories sit at the
front of PATH on many machines and are writable, and anything dropped there
is deleted on the next rehash — silently, days later.

Debug builds get the environment half only. `target/debug` holds a `tty7`
too, so otherwise a `cargo run` would repoint the developer's real `tty7`
at a debug binary, and each isolated dev-verify instance would rewrite the
PATH of the machine it is meant to stay away from.
2026-07-31 16:49:22 +08:00
thomasandClaude Fable 5 10b6741368 refactor: rename the GUI binary to tty7-app, freeing tty7 for the CLI
The package name and every display name ("tty7" in menus, tray, .desktop
Name, CFBundleName, installer AppName, shortcuts) stay as they were; only
the executable file is now tty7-app / tty7-app.exe, per docs/cli-design.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014JPaaZVK7rfQPKyrymzsYv
2026-07-31 09:28:00 +08:00
l0ng-ai 208454e202 feat(remote): remote workspaces — a window that is one machine
Split the framework-free half of tty7 into `tty7-core` and add a headless
`tty7-server` built on it, so a workspace's filesystem, git and session state
can live on another machine while the GUI stays where it is.

- `crates/tty7-core`: wire protocol, session daemon, PTY, native SSH engine and
  the domain model, with no gpui dependency. Module paths are unchanged.
- `crates/tty7-server`: the same daemon with no GUI attached, linked fully
  static against musl and pushed onto the remote box. One dependency, on
  purpose — a second one the GUI also needs belongs in core.
- `Host` trait + `HostId`/`HostRegistry`: every fs/git/watch call a workspace
  makes goes through the machine it belongs to. `LocalHost` answers on this
  box, `RemoteHost` over a routed control connection.
- `ui::host_ops`: the GUI's single door to a `Host`. Host calls block, so all
  of them run on the background executor with the result landed on the UI
  thread; de-duplication, staleness and error reporting live here rather than
  at each call site. Enforced by a CI grep.
- Connect flow: home page → pick a configured SSH host → the machine's own
  workspace list → a window bound to one workspace on it. Workspace switcher
  groups by machine, this computer included.
- CI: static musl builds of `tty7-server` for x86_64/aarch64 via
  cargo-zigbuild, a host-boundary grep, and version stamping factored out of
  the nightly workflow. Both new jobs are non-required so branch protection
  does not wedge open PRs.

Design and the interface contract it was built to are in
`docs/2026-07-27-remote-workspace-{design,impl-contract}.md`.
2026-07-28 10:59:46 +08:00
l0ng-ai 22e1ab1694 tty7: a GPU-rendered, daemon-backed terminal in pure Rust
tty7 is split into two Rust processes: a persistent daemon that owns the
shells and a GPU-rendered client that talks to it over a local socket.
Because the shells live in the daemon, quitting and reopening the app
leaves the session intact — detach and reattach, no tmux required.

- Persistent sessions — the daemon holds the PTYs and child processes, so
  closing a window or swapping in a new build never takes a shell down.
- Performance — an 11 MB `cat` completes in 95 ms and DOOM-fire renders at
  888 fps; the daemon drains the PTY at device speed off the render path.
- Shell-aware — new tabs and splits open in the current working directory;
  zsh, bash, fish, and PowerShell are set up automatically.
- Enhanced prompt — inline completion, syntax highlighting, history, and
  in-terminal search, with rich flag/subcommand signatures for common tools.
- Tabs, resizable splits, a command palette, click-to-open links, desktop
  notifications, eight themes, and CJK/IME input.

Native builds for macOS, Windows, and Linux.
Built on Zed's gpui and Alacritty's VT core.
2026-07-06 21:54:27 +08:00