Ports ZIG/src/cli into ghostty-cli: action detection/dispatch with per-action --help, and the actions help, version, list-actions, list-colors, list-keybinds, list-themes (plain listing plus an interactive ratatui browser mirroring Ghostty's libvaxis UI), show-config, validate-config, edit-config, crash-report (listing), ssh-cache and boo (235 embedded frames). Verified byte-for-byte against the installed Ghostty 1.3.1 for stdout, stderr and exit codes of most actions, incl. tty output in a pty at widths 20-200. Known differences: +list-colors --plain prints all 782 colors (Ghostty truncates at 617 because it never flushes), +version has placeholder build info until build_info::set is wired up. Deferred: list-fonts, show-face (need font discovery integration) and new-window (needs GTK IPC); they return NotYetAvailable for now. Test Plan: - cargo test -p ghostty-cli (86 tests) - cargo run -p ghostty-cli --example cli -- +list-keybinds --plain and diff against `ghostty +list-keybinds --plain`
6.0 KiB
ghostty-cli port notes (C1)
Crate crates/ghostty-cli (deps: ghostty-config, ghostty-input, ghostty-os; plus
ratatui/crossterm for TUIs, flate2 for boo frames, unicode-width). Run end to end with
cargo run -p ghostty-cli --example cli -- +list-actions --docs (the example behaves
like ghostty for actions via ghostty_cli::run_from_args, which also reproduces
main_ghostty.zig's "multiple/unknown action" errors). Do not edit the root crate:
wire run_from_args(&std::env::args().collect::<Vec<_>>()) into its main later.
API
Every action module exposes pub fn run(args: &[String]) -> Result<u8, Error>; args is the
full argv (argv[0] skipped, +action args ignored, like args.argsIterator). --help/-h
yields Error::ActionHelpRequested, which Action::run turns into the help text (stdout, exit 0).
Action::{ALL,name,from_name,file,help_text,run}, action::{detect_iter,ActionEnum,SpecialCase}.
File -> module
| Zig | Rust |
|---|---|
| cli.zig / action.zig | action.rs (generic detection + 7 tests) |
| ghostty.zig | ghostty.rs (Action, dispatch, help, run_from_args; 5 tests) |
| help.zig, version.zig | help.rs, version.rs (+ build_info.rs) |
| list_actions/list_colors/list_keybinds | same names; pretty output via tui.rs |
| list_themes.zig | list_themes.rs (listing) + list_themes/preview.rs (ratatui TUI) |
| show_config/validate_config/edit_config/crash_report | same names |
| ssh_cache.zig + ssh-cache/{Entry,DiskCache} | ssh_cache.rs, ssh_cache/{entry,disk_cache}.rs |
| boo.zig (+ build/framegen/frames) | boo.rs, boo_frames.deflate (235 frames, raw deflate of frames joined by \x01) |
| tui.zig | tui.rs (+ a small vaxis prettyPrint clone) |
| list_fonts/show_face/new_window | stubs: Options + help + Error::NotYetAvailable (TODO(integration)) |
Help texts (help/*.txt) are the run doc comments of the Zig files, extracted with the
helpgen rules (common-indent strip); --help output is byte-identical to Ghostty 1.3.1.
Oracle verification (Ghostty 1.3.1 at /usr/bin/ghostty)
Byte-identical (stdout, stderr, exit code): +list-actions (+--docs, --help), +list-keybinds
(--plain, --default, --docs; also with a custom config containing chords, chains, tables,
catch_all, unicode keys), +list-themes --plain (--path, --color=dark|light; with
GHOSTTY_RESOURCES_DIR=/usr/share/ghostty), +help, --help, -h, +validate-config (ok and
missing file), +crash-report, +ssh-cache, bad action / multiple actions / bad flag errors,
--help of every action, +show-config (except the two known config deviations: auto-update-channel
tip vs stable, gtk-opengl-debug debug build). Pretty (tty) output of +list-colors and
+list-keybinds is byte-identical in a pty for widths 20..200 (including line wrapping and
the first-row/teardown sequences of libvaxis); verified with a pty harness.
Known differences:
+list-colors --plain: Ghostty 1.3.1 never flushes its 4 KiB stdout buffer on this path, so its output is truncated at 617 of 782 lines. We print all 782 (first 617 identical).+list-colorsin a tty narrower than one column: Zig divides by zero; we clamp to one column.+versiondiffers in version/build info (placeholders inbuild_info; TODO(integration): root crate / apprt-gtk should callbuild_info::setwith the real GTK/libadwaita versions, commit and channel). "Zig version" row became "Rust version", "libxev" row printsn/a.+edit-configexec failure printserror.FileNotFound/error.AccessDeniedfor those kinds, otherwise the Rust io error text.+new-window: the Zig parse hook swallows--help; ported as is. IPC not available.
Interactive +list-themes (ratatui)
Same layout as the vaxis version: 32-column list with ❯ name ❮ selection, preview (header, 16
color swatches with decimal/hex numbers, 24-line code sample, lorem ipsum filler, diagnostics /
load-error panels), help (60x22), search (3-high box at bottom) and save (92x17) overlays, all
key bindings (q/Esc/^C, ?/F1/^H, /, ^X ^/, j/k/arrows, PgUp/PgDn (20), Home/End, h/x/d hex
toggle, c / C copy name/path via OSC 52, f cycle filter, Enter save dialog, w write
auto/theme.ghostty), mouse wheel / click / hover. Differences: no OSC 8 hyperlinks on names,
double/dashed/dotted/curly underlines drawn as single, color scheme not queried live (defaults to
light like Zig; COLORFGBG used as a hint), theme load errors during filtering are ignored
(Zig aborts), zf fuzzy ranking replaced by a subsequence matcher (only matching, not order, is
used). Logic (filtering, navigation, windowing, search, save) is unit tested; rendering is tested
against a ratatui Buffer.
Tests
Ported every Zig test in the ported files: action.zig (7 detect tests), ghostty.zig (5 parse action tests), ssh-cache/Entry.zig (6), DiskCache.zig (4 operation tests + isValidHost, isValidUser, isValidCacheKey). Skipped: none. 86 tests total including new ones (help/version output, show-config, validate-config, crash-report, ssh-cache action, list-colors/keybinds/themes, tui row rendering vs vaxis bytes, boo frame parsing of all 235 frames, preview state machine, new-window arg hook).
TODO(integration) / gaps
list-fonts,show-face(need ghostty-font),new-window(GTK IPC): returnError::NotYetAvailable.build_info::setfor real version/GTK info.- ghostty-vt
x11_colorlacks a key listing and loses name case:list_colorsre-parsesghostty-vt/src/res/rgb.txtthrough a relativeinclude_str!path. - ghostty-input
ActionKey::docs()has an extra trailing blank line for 4 actions (adjust_selection, ...) compared to Ghostty's help strings;list_actions::generate_plaintextis a local copy ofhelpgen_actions::generatethat normalises trailing newlines. Fix the data in ghostty-input and switch back tohelpgen_actions::generate. - crash-report only lists (sentry is not ported).
ssh-cachelocking usesFile::try_lock; like Zig, a lock held by another process is ignored (the boolean result is unused upstream). Config::loadandfinalizeare called twice invalidate-config(as in Zig).