From 4cc663fc6003aa6626d4a9c73f1763b862dc569f Mon Sep 17 00:00:00 2001 From: Matthew Hrehirchuk Date: Tue, 30 Sep 2025 11:23:02 -0600 Subject: [PATCH 1/4] feat: add GHOSTTY_BIN_DIR to path via shell integration --- src/config/Config.zig | 6 ++++++ src/shell-integration/bash/ghostty.bash | 8 ++++++++ .../elvish/lib/ghostty-integration.elv | 13 +++++++++++++ .../vendor_conf.d/ghostty-shell-integration.fish | 8 ++++++++ src/shell-integration/zsh/ghostty-integration | 8 ++++++++ src/termio/shell_integration.zig | 8 ++++---- 6 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/config/Config.zig b/src/config/Config.zig index 46eb03fe2..b7a9699c8 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -2359,6 +2359,11 @@ keybind: Keybinds = .{}, /// cache manually using various arguments. /// (Available since: 1.2.0) /// +/// * `path` - Add Ghostty's binary directory to PATH. This ensures the `ghostty` +/// command is available in the shell even if shell init scripts reset PATH. +/// This is particularly useful on macOS where PATH is often overridden by +/// system scripts. The directory is only added if not already present. +/// /// SSH features work independently and can be combined for optimal experience: /// when both `ssh-env` and `ssh-terminfo` are enabled, Ghostty will install its /// terminfo on remote hosts and use `xterm-ghostty` as TERM, falling back to @@ -6994,6 +6999,7 @@ pub const ShellIntegrationFeatures = packed struct { title: bool = true, @"ssh-env": bool = false, @"ssh-terminfo": bool = false, + path: bool = true, }; pub const RepeatableCommand = struct { diff --git a/src/shell-integration/bash/ghostty.bash b/src/shell-integration/bash/ghostty.bash index 2cf9d388f..cdaddea5c 100644 --- a/src/shell-integration/bash/ghostty.bash +++ b/src/shell-integration/bash/ghostty.bash @@ -73,6 +73,14 @@ if [ -n "$GHOSTTY_BASH_INJECT" ]; then builtin unset GHOSTTY_BASH_RCFILE fi +# Add Ghostty binary to PATH if the path feature is enabled +if [[ "$GHOSTTY_SHELL_FEATURES" == *"path"* && -n "$GHOSTTY_BIN_DIR" ]]; then + # Check if the directory is already in PATH + if [[ ":$PATH:" != *":$GHOSTTY_BIN_DIR:"* ]]; then + export PATH="$PATH:$GHOSTTY_BIN_DIR" + fi +fi + # Sudo if [[ "$GHOSTTY_SHELL_FEATURES" == *"sudo"* && -n "$TERMINFO" ]]; then # Wrap `sudo` command to ensure Ghostty terminfo is preserved. diff --git a/src/shell-integration/elvish/lib/ghostty-integration.elv b/src/shell-integration/elvish/lib/ghostty-integration.elv index 6d0d19f4f..e570e3aec 100644 --- a/src/shell-integration/elvish/lib/ghostty-integration.elv +++ b/src/shell-integration/elvish/lib/ghostty-integration.elv @@ -196,6 +196,19 @@ set edit:before-readline = (conj $edit:before-readline $beam~) set edit:after-readline = (conj $edit:after-readline {|_| block }) } + if (and (has-value $features path) (has-env GHOSTTY_BIN_DIR)) { + # Check if the directory is already in PATH + var path-contains-ghostty = $false + for p $paths { + if (eq $p $E:GHOSTTY_BIN_DIR) { + set path-contains-ghostty = $true + break + } + } + if (not $path-contains-ghostty) { + set paths = [$@paths $E:GHOSTTY_BIN_DIR] + } + } if (and (has-value $features sudo) (not-eq "" $E:TERMINFO) (has-external sudo)) { edit:add-var sudo~ $sudo-with-terminfo~ } diff --git a/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish b/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish index daa4f1d4f..fcba767a2 100644 --- a/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish +++ b/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish @@ -61,6 +61,14 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration" end end + # Add Ghostty binary to PATH if the path feature is enabled + if contains path $features; and test -n "$GHOSTTY_BIN_DIR" + # Check if the directory is already in PATH + if not contains -- "$GHOSTTY_BIN_DIR" $PATH + set --global --export PATH $PATH "$GHOSTTY_BIN_DIR" + end + end + # When using sudo shell integration feature, ensure $TERMINFO is set # and `sudo` is not already a function or alias if contains sudo $features; and test -n "$TERMINFO"; and test "file" = (type -t sudo 2> /dev/null; or echo "x") diff --git a/src/shell-integration/zsh/ghostty-integration b/src/shell-integration/zsh/ghostty-integration index 8607664a2..e76aace4a 100644 --- a/src/shell-integration/zsh/ghostty-integration +++ b/src/shell-integration/zsh/ghostty-integration @@ -220,6 +220,14 @@ _ghostty_deferred_init() { builtin print -rnu $_ghostty_fd \$'\\e[0 q'" fi + # Add Ghostty binary to PATH if the path feature is enabled + if [[ "$GHOSTTY_SHELL_FEATURES" == *"path"* ]] && [[ -n "$GHOSTTY_BIN_DIR" ]]; then + # Check if the directory is already in PATH + if [[ ":$PATH:" != *":$GHOSTTY_BIN_DIR:"* ]]; then + builtin export PATH="$PATH:$GHOSTTY_BIN_DIR" + fi + fi + # Sudo if [[ "$GHOSTTY_SHELL_FEATURES" == *"sudo"* ]] && [[ -n "$TERMINFO" ]]; then # Wrap `sudo` command to ensure Ghostty terminfo is preserved diff --git a/src/termio/shell_integration.zig b/src/termio/shell_integration.zig index 30519b6e2..90a697409 100644 --- a/src/termio/shell_integration.zig +++ b/src/termio/shell_integration.zig @@ -219,8 +219,8 @@ test "setup features" { var env = EnvMap.init(alloc); defer env.deinit(); - try setupFeatures(&env, .{ .cursor = true, .sudo = true, .title = true, .@"ssh-env" = true, .@"ssh-terminfo" = true }); - try testing.expectEqualStrings("cursor,ssh-env,ssh-terminfo,sudo,title", env.get("GHOSTTY_SHELL_FEATURES").?); + try setupFeatures(&env, .{ .cursor = true, .sudo = true, .title = true, .@"ssh-env" = true, .@"ssh-terminfo" = true, .path = true }); + try testing.expectEqualStrings("cursor,path,ssh-env,ssh-terminfo,sudo,title", env.get("GHOSTTY_SHELL_FEATURES").?); } // Test: all features disabled @@ -228,7 +228,7 @@ test "setup features" { var env = EnvMap.init(alloc); defer env.deinit(); - try setupFeatures(&env, .{ .cursor = false, .sudo = false, .title = false, .@"ssh-env" = false, .@"ssh-terminfo" = false }); + try setupFeatures(&env, .{ .cursor = false, .sudo = false, .title = false, .@"ssh-env" = false, .@"ssh-terminfo" = false, .path = false }); try testing.expect(env.get("GHOSTTY_SHELL_FEATURES") == null); } @@ -237,7 +237,7 @@ test "setup features" { var env = EnvMap.init(alloc); defer env.deinit(); - try setupFeatures(&env, .{ .cursor = false, .sudo = true, .title = false, .@"ssh-env" = true, .@"ssh-terminfo" = false }); + try setupFeatures(&env, .{ .cursor = false, .sudo = true, .title = false, .@"ssh-env" = true, .@"ssh-terminfo" = false, .path = false }); try testing.expectEqualStrings("ssh-env,sudo", env.get("GHOSTTY_SHELL_FEATURES").?); } } From 9407e0fd0d7bc7243b5eaf04658a591cd77e41a3 Mon Sep 17 00:00:00 2001 From: Matthew Hrehirchuk Date: Tue, 30 Sep 2025 22:26:07 -0600 Subject: [PATCH 2/4] fix: cleaned up elvish and fish integrations for bin_dir --- .../elvish/lib/ghostty-integration.elv | 11 ++--------- .../fish/vendor_conf.d/ghostty-shell-integration.fish | 5 +---- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/shell-integration/elvish/lib/ghostty-integration.elv b/src/shell-integration/elvish/lib/ghostty-integration.elv index e570e3aec..04fe8f86e 100644 --- a/src/shell-integration/elvish/lib/ghostty-integration.elv +++ b/src/shell-integration/elvish/lib/ghostty-integration.elv @@ -198,15 +198,8 @@ } if (and (has-value $features path) (has-env GHOSTTY_BIN_DIR)) { # Check if the directory is already in PATH - var path-contains-ghostty = $false - for p $paths { - if (eq $p $E:GHOSTTY_BIN_DIR) { - set path-contains-ghostty = $true - break - } - } - if (not $path-contains-ghostty) { - set paths = [$@paths $E:GHOSTTY_BIN_DIR] + if (not (has-value $paths $E:GHOSTTY_BIN_DIR)) { + set paths = [$E:GHOSTTY_BIN_DIR $@paths] } } if (and (has-value $features sudo) (not-eq "" $E:TERMINFO) (has-external sudo)) { diff --git a/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish b/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish index fcba767a2..b9add8cfd 100644 --- a/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish +++ b/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish @@ -63,10 +63,7 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration" # Add Ghostty binary to PATH if the path feature is enabled if contains path $features; and test -n "$GHOSTTY_BIN_DIR" - # Check if the directory is already in PATH - if not contains -- "$GHOSTTY_BIN_DIR" $PATH - set --global --export PATH $PATH "$GHOSTTY_BIN_DIR" - end + fish_add_path "$GHOSTTY_BIN_DIR" end # When using sudo shell integration feature, ensure $TERMINFO is set From 4989f92c719005b54cb17dfc20f76bbfbd946f5b Mon Sep 17 00:00:00 2001 From: Jon Parise Date: Wed, 1 Oct 2025 10:27:42 -0400 Subject: [PATCH 3/4] shell-integration: remove redundant comments I think the conditions are sufficiently self-descriptive. --- src/shell-integration/bash/ghostty.bash | 1 - src/shell-integration/elvish/lib/ghostty-integration.elv | 1 - src/shell-integration/zsh/ghostty-integration | 1 - 3 files changed, 3 deletions(-) diff --git a/src/shell-integration/bash/ghostty.bash b/src/shell-integration/bash/ghostty.bash index cdaddea5c..e910a9885 100644 --- a/src/shell-integration/bash/ghostty.bash +++ b/src/shell-integration/bash/ghostty.bash @@ -75,7 +75,6 @@ fi # Add Ghostty binary to PATH if the path feature is enabled if [[ "$GHOSTTY_SHELL_FEATURES" == *"path"* && -n "$GHOSTTY_BIN_DIR" ]]; then - # Check if the directory is already in PATH if [[ ":$PATH:" != *":$GHOSTTY_BIN_DIR:"* ]]; then export PATH="$PATH:$GHOSTTY_BIN_DIR" fi diff --git a/src/shell-integration/elvish/lib/ghostty-integration.elv b/src/shell-integration/elvish/lib/ghostty-integration.elv index 04fe8f86e..11f4eae5b 100644 --- a/src/shell-integration/elvish/lib/ghostty-integration.elv +++ b/src/shell-integration/elvish/lib/ghostty-integration.elv @@ -197,7 +197,6 @@ set edit:after-readline = (conj $edit:after-readline {|_| block }) } if (and (has-value $features path) (has-env GHOSTTY_BIN_DIR)) { - # Check if the directory is already in PATH if (not (has-value $paths $E:GHOSTTY_BIN_DIR)) { set paths = [$E:GHOSTTY_BIN_DIR $@paths] } diff --git a/src/shell-integration/zsh/ghostty-integration b/src/shell-integration/zsh/ghostty-integration index e76aace4a..27ef39bbc 100644 --- a/src/shell-integration/zsh/ghostty-integration +++ b/src/shell-integration/zsh/ghostty-integration @@ -222,7 +222,6 @@ _ghostty_deferred_init() { # Add Ghostty binary to PATH if the path feature is enabled if [[ "$GHOSTTY_SHELL_FEATURES" == *"path"* ]] && [[ -n "$GHOSTTY_BIN_DIR" ]]; then - # Check if the directory is already in PATH if [[ ":$PATH:" != *":$GHOSTTY_BIN_DIR:"* ]]; then builtin export PATH="$PATH:$GHOSTTY_BIN_DIR" fi From 6f596ee7c353706d0bba4088eb43c31e4dd677b9 Mon Sep 17 00:00:00 2001 From: Jon Parise Date: Wed, 1 Oct 2025 10:42:33 -0400 Subject: [PATCH 4/4] shell-integration: append $GHOSTTY_BIN_DIR to $PATH For consistency with the termio/Exec.zig implementation, we always append to the PATH (lowest priority). --- src/shell-integration/elvish/lib/ghostty-integration.elv | 2 +- .../fish/vendor_conf.d/ghostty-shell-integration.fish | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shell-integration/elvish/lib/ghostty-integration.elv b/src/shell-integration/elvish/lib/ghostty-integration.elv index 11f4eae5b..33473c8b0 100644 --- a/src/shell-integration/elvish/lib/ghostty-integration.elv +++ b/src/shell-integration/elvish/lib/ghostty-integration.elv @@ -198,7 +198,7 @@ } if (and (has-value $features path) (has-env GHOSTTY_BIN_DIR)) { if (not (has-value $paths $E:GHOSTTY_BIN_DIR)) { - set paths = [$E:GHOSTTY_BIN_DIR $@paths] + set paths = [$@paths $E:GHOSTTY_BIN_DIR] } } if (and (has-value $features sudo) (not-eq "" $E:TERMINFO) (has-external sudo)) { diff --git a/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish b/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish index b9add8cfd..7042f892a 100644 --- a/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish +++ b/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish @@ -63,7 +63,7 @@ function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration" # Add Ghostty binary to PATH if the path feature is enabled if contains path $features; and test -n "$GHOSTTY_BIN_DIR" - fish_add_path "$GHOSTTY_BIN_DIR" + fish_add_path --append "$GHOSTTY_BIN_DIR" end # When using sudo shell integration feature, ensure $TERMINFO is set