aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/shell-integration
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-04-04 19:22:04 +0300
committerCan Celik <ogulcancelik@gmail.com>2026-04-05 01:23:06 +0300
commit41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch)
tree3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/shell-integration
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/shell-integration')
-rw-r--r--vendor/libghostty-vt/src/shell-integration/README.md117
-rw-r--r--vendor/libghostty-vt/src/shell-integration/bash/bash-preexec.sh382
-rw-r--r--vendor/libghostty-vt/src/shell-integration/bash/ghostty.bash324
-rw-r--r--vendor/libghostty-vt/src/shell-integration/elvish/lib/ghostty-integration.elv190
-rw-r--r--vendor/libghostty-vt/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish245
-rw-r--r--vendor/libghostty-vt/src/shell-integration/nushell/vendor/autoload/ghostty.nu110
-rw-r--r--vendor/libghostty-vt/src/shell-integration/zsh/.zshenv61
-rw-r--r--vendor/libghostty-vt/src/shell-integration/zsh/ghostty-integration454
8 files changed, 1883 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/shell-integration/README.md b/vendor/libghostty-vt/src/shell-integration/README.md
new file mode 100644
index 00000000..590632da
--- /dev/null
+++ b/vendor/libghostty-vt/src/shell-integration/README.md
@@ -0,0 +1,117 @@
+# Shell Integration Code
+
+This is the shell-specific shell-integration code that is
+used for the shell-integration feature set that Ghostty
+supports.
+
+This README is meant as developer documentation and not as
+user documentation. For user documentation, see the main
+README or [ghostty.org](https://ghostty.org/docs)
+
+## Implementation Details
+
+### Bash
+
+Automatic [Bash](https://www.gnu.org/software/bash/) shell integration works by
+starting Bash in POSIX mode and using the `ENV` environment variable to load
+our integration script (`bash/ghostty.bash`). This prevents Bash from loading
+its normal startup files, which becomes our script's responsibility (along with
+disabling POSIX mode).
+
+Bash shell integration can also be sourced manually from `bash/ghostty.bash`.
+This also works for older versions of Bash.
+
+```bash
+# Ghostty shell integration for Bash. This must be at the top of your bashrc!
+if [ -n "${GHOSTTY_RESOURCES_DIR}" ]; then
+ builtin source "${GHOSTTY_RESOURCES_DIR}/shell-integration/bash/ghostty.bash"
+fi
+```
+
+> [!NOTE]
+>
+> The version of Bash distributed with macOS (`/bin/bash`) does not support
+> automatic shell integration. You'll need to manually source the shell
+> integration script (as shown above). You can also install a standard
+> version of Bash from Homebrew or elsewhere and set it as your shell.
+
+### Elvish
+
+For [Elvish](https://elv.sh), `$GHOSTTY_RESOURCES_DIR/src/shell-integration`
+contains an `./elvish/lib/ghostty-integration.elv` file.
+
+Elvish, on startup, searches for paths defined in `XDG_DATA_DIRS`
+variable for `./elvish/lib/*.elv` files and imports them. They are thus
+made available for use as modules by way of `use <filename>`.
+
+Ghostty launches Elvish, passing the environment with `XDG_DATA_DIRS` prepended
+with `$GHOSTTY_RESOURCES_DIR/src/shell-integration`. It contains
+`./elvish/lib/ghostty-integration.elv`. The user can then import it
+by `use ghostty-integration` every time after shell startup or
+autostart integration in `$XDG_CONFIG_HOME/elvish/rc.elv`,
+which will run the integration routines.
+
+If you decide to autostart `ghostty-integration` with `rc.elv`, you should
+detect whether the terminal is Ghostty or not. To do this, add this to the end
+of your `rc.elv` file:
+
+```elvish
+if (eq $E:TERM "xterm-ghostty") {
+ try { use ghostty-integration } catch { }
+}
+```
+
+The [Elvish](https://elv.sh) shell integration is supported by
+the community and is not officially supported by Ghostty. We distribute
+it for ease of access and use but do not provide support for it.
+If you experience issues with the Elvish shell integration, I welcome
+any contributions to fix them. Thank you!
+
+### Fish
+
+For [Fish](https://fishshell.com/), Ghostty prepends to the
+`XDG_DATA_DIRS` directory. Fish automatically loads configuration
+files in `<XDG_DATA_DIR>/fish/vendor_conf.d/*.fish` on startup,
+allowing us to automatically integrate with the shell. For details
+on the Fish startup process, see the
+[Fish documentation](https://fishshell.com/docs/current/language.html).
+
+### Nushell
+
+For [Nushell](https://www.nushell.sh/), Ghostty prepends to the
+`XDG_DATA_DIRS` directory, making the `ghostty` module available through
+Nushell's vendor autoload mechanism. Ghostty then automatically imports
+the module using the `-e "use ghostty *"` flag when starting Nushell.
+
+Nushell provides many shell features itself, such as `title` and `cursor`,
+so our integration focuses on Ghostty-specific features like `sudo`,
+`ssh-env`, and `ssh-terminfo`.
+
+The shell integration is automatically enabled when running Nushell in Ghostty,
+but you can also load it manually is shell integration is disabled:
+
+```nushell
+source $GHOSTTY_RESOURCES_DIR/shell-integration/nushell/vendor/autoload/ghostty.nu
+use ghostty *
+```
+
+### Zsh
+
+Automatic [Zsh](https://www.zsh.org/) integration works by temporarily setting
+`ZDOTDIR` to our `zsh` directory. An existing `ZDOTDIR` environment variable
+value will be retained and restored after our shell integration scripts are
+run.
+
+However, if `ZDOTDIR` is set in a system-wide file like `/etc/zshenv`, it will
+override Ghostty's `ZDOTDIR` value, preventing the shell integration from being
+loaded. In this case, the shell integration needs to be loaded manually.
+
+To load the Zsh shell integration manually:
+
+```zsh
+if [[ -n $GHOSTTY_RESOURCES_DIR ]]; then
+ source "$GHOSTTY_RESOURCES_DIR"/shell-integration/zsh/ghostty-integration
+fi
+```
+
+Shell integration requires Zsh 5.1+.
diff --git a/vendor/libghostty-vt/src/shell-integration/bash/bash-preexec.sh b/vendor/libghostty-vt/src/shell-integration/bash/bash-preexec.sh
new file mode 100644
index 00000000..f20ece17
--- /dev/null
+++ b/vendor/libghostty-vt/src/shell-integration/bash/bash-preexec.sh
@@ -0,0 +1,382 @@
+# bash-preexec.sh -- Bash support for ZSH-like 'preexec' and 'precmd' functions.
+# https://github.com/rcaloras/bash-preexec
+#
+#
+# 'preexec' functions are executed before each interactive command is
+# executed, with the interactive command as its argument. The 'precmd'
+# function is executed before each prompt is displayed.
+#
+# Author: Ryan Caloras (ryan@bashhub.com)
+# Forked from Original Author: Glyph Lefkowitz
+#
+# V0.6.0
+#
+
+# General Usage:
+#
+# 1. Source this file at the end of your bash profile so as not to interfere
+# with anything else that's using PROMPT_COMMAND.
+#
+# 2. Add any precmd or preexec functions by appending them to their arrays:
+# e.g.
+# precmd_functions+=(my_precmd_function)
+# precmd_functions+=(some_other_precmd_function)
+#
+# preexec_functions+=(my_preexec_function)
+#
+# 3. Consider changing anything using the DEBUG trap or PROMPT_COMMAND
+# to use preexec and precmd instead. Preexisting usages will be
+# preserved, but doing so manually may be less surprising.
+#
+# Note: This module requires two Bash features which you must not otherwise be
+# using: the "DEBUG" trap, and the "PROMPT_COMMAND" variable. If you override
+# either of these after bash-preexec has been installed it will most likely break.
+
+# Tell shellcheck what kind of file this is.
+# shellcheck shell=bash
+
+# Make sure this is bash that's running and return otherwise.
+# Use POSIX syntax for this line:
+if [ -z "${BASH_VERSION-}" ]; then
+ return 1
+fi
+
+# We only support Bash 3.1+.
+# Note: BASH_VERSINFO is first available in Bash-2.0.
+if [[ -z "${BASH_VERSINFO-}" ]] || (( BASH_VERSINFO[0] < 3 || (BASH_VERSINFO[0] == 3 && BASH_VERSINFO[1] < 1) )); then
+ return 1
+fi
+
+# Avoid duplicate inclusion
+if [[ -n "${bash_preexec_imported:-}" || -n "${__bp_imported:-}" ]]; then
+ return 0
+fi
+bash_preexec_imported="defined"
+
+# WARNING: This variable is no longer used and should not be relied upon.
+# Use ${bash_preexec_imported} instead.
+# shellcheck disable=SC2034
+__bp_imported="${bash_preexec_imported}"
+
+# Should be available to each precmd and preexec
+# functions, should they want it. $? and $_ are available as $? and $_, but
+# $PIPESTATUS is available only in a copy, $BP_PIPESTATUS.
+# TODO: Figure out how to restore PIPESTATUS before each precmd or preexec
+# function.
+__bp_last_ret_value="$?"
+BP_PIPESTATUS=("${PIPESTATUS[@]}")
+__bp_last_argument_prev_command="$_"
+
+__bp_inside_precmd=0
+__bp_inside_preexec=0
+
+# Initial PROMPT_COMMAND string that is removed from PROMPT_COMMAND post __bp_install
+__bp_install_string=$'__bp_trap_string="$(trap -p DEBUG)"\ntrap - DEBUG\n__bp_install'
+
+# Fails if any of the given variables are readonly
+# Reference https://stackoverflow.com/a/4441178
+__bp_require_not_readonly() {
+ local var
+ for var; do
+ if ! ( unset "$var" 2> /dev/null ); then
+ echo "bash-preexec requires write access to ${var}" >&2
+ return 1
+ fi
+ done
+}
+
+# Remove ignorespace and or replace ignoreboth from HISTCONTROL
+# so we can accurately invoke preexec with a command from our
+# history even if it starts with a space.
+__bp_adjust_histcontrol() {
+ local histcontrol
+ histcontrol="${HISTCONTROL:-}"
+ histcontrol="${histcontrol//ignorespace}"
+ # Replace ignoreboth with ignoredups
+ if [[ "$histcontrol" == *"ignoreboth"* ]]; then
+ histcontrol="ignoredups:${histcontrol//ignoreboth}"
+ fi
+ export HISTCONTROL="$histcontrol"
+}
+
+# This variable describes whether we are currently in "interactive mode";
+# i.e. whether this shell has just executed a prompt and is waiting for user
+# input. It documents whether the current command invoked by the trace hook is
+# run interactively by the user; it's set immediately after the prompt hook,
+# and unset as soon as the trace hook is run.
+__bp_preexec_interactive_mode=""
+
+# These arrays are used to add functions to be run before, or after, prompts.
+declare -a precmd_functions
+declare -a preexec_functions
+
+# Trims leading and trailing whitespace from $2 and writes it to the variable
+# name passed as $1
+__bp_trim_whitespace() {
+ local var=${1:?} text=${2:-}
+ text="${text#"${text%%[![:space:]]*}"}" # remove leading whitespace characters
+ text="${text%"${text##*[![:space:]]}"}" # remove trailing whitespace characters
+ printf -v "$var" '%s' "$text"
+}
+
+
+# Trims whitespace and removes any leading or trailing semicolons from $2 and
+# writes the resulting string to the variable name passed as $1. Used for
+# manipulating substrings in PROMPT_COMMAND
+__bp_sanitize_string() {
+ local var=${1:?} text=${2:-} sanitized
+ __bp_trim_whitespace sanitized "$text"
+ sanitized=${sanitized%;}
+ sanitized=${sanitized#;}
+ __bp_trim_whitespace sanitized "$sanitized"
+ printf -v "$var" '%s' "$sanitized"
+}
+
+# This function is installed as part of the PROMPT_COMMAND;
+# It sets a variable to indicate that the prompt was just displayed,
+# to allow the DEBUG trap to know that the next command is likely interactive.
+__bp_interactive_mode() {
+ __bp_preexec_interactive_mode="on"
+}
+
+
+# This function is installed as part of the PROMPT_COMMAND.
+# It will invoke any functions defined in the precmd_functions array.
+__bp_precmd_invoke_cmd() {
+ # Save the returned value from our last command, and from each process in
+ # its pipeline. Note: this MUST be the first thing done in this function.
+ # BP_PIPESTATUS may be unused, ignore
+ # shellcheck disable=SC2034
+
+ __bp_last_ret_value="$?" BP_PIPESTATUS=("${PIPESTATUS[@]}")
+
+ # Don't invoke precmds if we are inside an execution of an "original
+ # prompt command" by another precmd execution loop. This avoids infinite
+ # recursion.
+ if (( __bp_inside_precmd > 0 )); then
+ return
+ fi
+ local __bp_inside_precmd=1
+
+ # Invoke every function defined in our function array.
+ local precmd_function
+ for precmd_function in "${precmd_functions[@]}"; do
+
+ # Only execute this function if it actually exists.
+ # Test existence of functions with: declare -[Ff]
+ if type -t "$precmd_function" 1>/dev/null; then
+ __bp_set_ret_value "$__bp_last_ret_value" "$__bp_last_argument_prev_command"
+ # Quote our function invocation to prevent issues with IFS
+ "$precmd_function"
+ fi
+ done
+
+ __bp_set_ret_value "$__bp_last_ret_value"
+}
+
+# Sets a return value in $?. We may want to get access to the $? variable in our
+# precmd functions. This is available for instance in zsh. We can simulate it in bash
+# by setting the value here.
+__bp_set_ret_value() {
+ return ${1:+"$1"}
+}
+
+__bp_in_prompt_command() {
+
+ local prompt_command_array IFS=$'\n;'
+ read -rd '' -a prompt_command_array <<< "${PROMPT_COMMAND[*]:-}"
+
+ local trimmed_arg
+ __bp_trim_whitespace trimmed_arg "${1:-}"
+
+ local command trimmed_command
+ for command in "${prompt_command_array[@]:-}"; do
+ __bp_trim_whitespace trimmed_command "$command"
+ if [[ "$trimmed_command" == "$trimmed_arg" ]]; then
+ return 0
+ fi
+ done
+
+ return 1
+}
+
+# This function is installed as the DEBUG trap. It is invoked before each
+# interactive prompt display. Its purpose is to inspect the current
+# environment to attempt to detect if the current command is being invoked
+# interactively, and invoke 'preexec' if so.
+__bp_preexec_invoke_exec() {
+
+ # Save the contents of $_ so that it can be restored later on.
+ # https://stackoverflow.com/questions/40944532/bash-preserve-in-a-debug-trap#40944702
+ __bp_last_argument_prev_command="${1:-}"
+ # Don't invoke preexecs if we are inside of another preexec.
+ if (( __bp_inside_preexec > 0 )); then
+ return
+ fi
+ local __bp_inside_preexec=1
+
+ # Checks if the file descriptor is not standard out (i.e. '1')
+ # __bp_delay_install checks if we're in test. Needed for bats to run.
+ # Prevents preexec from being invoked for functions in PS1
+ if [[ ! -t 1 && -z "${__bp_delay_install:-}" ]]; then
+ return
+ fi
+
+ if [[ -n "${COMP_POINT:-}" || -n "${READLINE_POINT:-}" ]]; then
+ # We're in the middle of a completer or a keybinding set up by "bind
+ # -x". This obviously can't be an interactively issued command.
+ return
+ fi
+ if [[ -z "${__bp_preexec_interactive_mode:-}" ]]; then
+ # We're doing something related to displaying the prompt. Let the
+ # prompt set the title instead of me.
+ return
+ else
+ # If we're in a subshell, then the prompt won't be re-displayed to put
+ # us back into interactive mode, so let's not set the variable back.
+ # In other words, if you have a subshell like
+ # (sleep 1; sleep 2)
+ # You want to see the 'sleep 2' as a set_command_title as well.
+ if [[ 0 -eq "${BASH_SUBSHELL:-}" ]]; then
+ __bp_preexec_interactive_mode=""
+ fi
+ fi
+
+ if __bp_in_prompt_command "${BASH_COMMAND:-}"; then
+ # If we're executing something inside our prompt_command then we don't
+ # want to call preexec. Bash prior to 3.1 can't detect this at all :/
+ __bp_preexec_interactive_mode=""
+ return
+ fi
+
+ local this_command
+ this_command=$(LC_ALL=C HISTTIMEFORMAT='' builtin history 1)
+ this_command="${this_command#*[[:digit:]][* ] }"
+
+ # Sanity check to make sure we have something to invoke our function with.
+ if [[ -z "$this_command" ]]; then
+ return
+ fi
+
+ # Invoke every function defined in our function array.
+ local preexec_function
+ local preexec_function_ret_value
+ local preexec_ret_value=0
+ for preexec_function in "${preexec_functions[@]:-}"; do
+
+ # Only execute each function if it actually exists.
+ # Test existence of function with: declare -[fF]
+ if type -t "$preexec_function" 1>/dev/null; then
+ __bp_set_ret_value "${__bp_last_ret_value:-}"
+ # Quote our function invocation to prevent issues with IFS
+ "$preexec_function" "$this_command"
+ preexec_function_ret_value="$?"
+ if [[ "$preexec_function_ret_value" != 0 ]]; then
+ preexec_ret_value="$preexec_function_ret_value"
+ fi
+ fi
+ done
+
+ # Restore the last argument of the last executed command, and set the return
+ # value of the DEBUG trap to be the return code of the last preexec function
+ # to return an error.
+ # If `extdebug` is enabled a non-zero return value from any preexec function
+ # will cause the user's command not to execute.
+ # Run `shopt -s extdebug` to enable
+ __bp_set_ret_value "$preexec_ret_value" "$__bp_last_argument_prev_command"
+}
+
+__bp_install() {
+ # Exit if we already have this installed.
+ if [[ "${PROMPT_COMMAND[*]:-}" == *"__bp_precmd_invoke_cmd"* ]]; then
+ return 1
+ fi
+
+ trap '__bp_preexec_invoke_exec "$_"' DEBUG
+
+ # Preserve any prior DEBUG trap as a preexec function
+ eval "local trap_argv=(${__bp_trap_string:-})"
+ local prior_trap=${trap_argv[2]:-}
+ unset __bp_trap_string
+ if [[ -n "$prior_trap" ]]; then
+ eval '__bp_original_debug_trap() {
+ '"$prior_trap"'
+ }'
+ preexec_functions+=(__bp_original_debug_trap)
+ fi
+
+ # Adjust our HISTCONTROL Variable if needed.
+ #
+ # GHOSTTY: Don't modify HISTCONTROL. This hack is only needed to improve the
+ # accuracy of the command argument passed to the preexec functions, and we
+ # don't use that argument in our bash shell integration script (and nor does
+ # the __bp_original_debug_trap function above, which is the only other active
+ # preexec function).
+ #__bp_adjust_histcontrol
+
+ # Issue #25. Setting debug trap for subshells causes sessions to exit for
+ # backgrounded subshell commands (e.g. (pwd)& ). Believe this is a bug in Bash.
+ #
+ # Disabling this by default. It can be enabled by setting this variable.
+ if [[ -n "${__bp_enable_subshells:-}" ]]; then
+
+ # Set so debug trap will work be invoked in subshells.
+ set -o functrace > /dev/null 2>&1
+ shopt -s extdebug > /dev/null 2>&1
+ fi
+
+ local existing_prompt_command
+ # Remove setting our trap install string and sanitize the existing prompt command string
+ existing_prompt_command="${PROMPT_COMMAND:-}"
+ # Edge case of appending to PROMPT_COMMAND
+ existing_prompt_command="${existing_prompt_command//$__bp_install_string/:}" # no-op
+ existing_prompt_command="${existing_prompt_command//$'\n':$'\n'/$'\n'}" # remove known-token only
+ existing_prompt_command="${existing_prompt_command//$'\n':;/$'\n'}" # remove known-token only
+ __bp_sanitize_string existing_prompt_command "$existing_prompt_command"
+ if [[ "${existing_prompt_command:-:}" == ":" ]]; then
+ existing_prompt_command=
+ fi
+
+ # Install our hooks in PROMPT_COMMAND to allow our trap to know when we've
+ # actually entered something.
+ PROMPT_COMMAND='__bp_precmd_invoke_cmd'
+ PROMPT_COMMAND+=${existing_prompt_command:+$'\n'$existing_prompt_command}
+ if (( BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 1) )); then
+ PROMPT_COMMAND+=('__bp_interactive_mode')
+ else
+ # shellcheck disable=SC2179 # PROMPT_COMMAND is not an array in bash <= 5.0
+ PROMPT_COMMAND+=$'\n__bp_interactive_mode'
+ fi
+
+ # Add two functions to our arrays for convenience
+ # of definition.
+ precmd_functions+=(precmd)
+ preexec_functions+=(preexec)
+
+ # Invoke our two functions manually that were added to $PROMPT_COMMAND
+ __bp_precmd_invoke_cmd
+ __bp_interactive_mode
+}
+
+# Sets an installation string as part of our PROMPT_COMMAND to install
+# after our session has started. This allows bash-preexec to be included
+# at any point in our bash profile.
+__bp_install_after_session_init() {
+ # bash-preexec needs to modify these variables in order to work correctly
+ # if it can't, just stop the installation
+ __bp_require_not_readonly PROMPT_COMMAND HISTCONTROL HISTTIMEFORMAT || return
+
+ local sanitized_prompt_command
+ __bp_sanitize_string sanitized_prompt_command "${PROMPT_COMMAND:-}"
+ if [[ -n "$sanitized_prompt_command" ]]; then
+ # shellcheck disable=SC2178 # PROMPT_COMMAND is not an array in bash <= 5.0
+ PROMPT_COMMAND=${sanitized_prompt_command}$'\n'
+ fi
+ # shellcheck disable=SC2179 # PROMPT_COMMAND is not an array in bash <= 5.0
+ PROMPT_COMMAND+=${__bp_install_string}
+}
+
+# Run our install so long as we're not delaying it.
+if [[ -z "${__bp_delay_install:-}" ]]; then
+ __bp_install_after_session_init
+fi
diff --git a/vendor/libghostty-vt/src/shell-integration/bash/ghostty.bash b/vendor/libghostty-vt/src/shell-integration/bash/ghostty.bash
new file mode 100644
index 00000000..7eaf1397
--- /dev/null
+++ b/vendor/libghostty-vt/src/shell-integration/bash/ghostty.bash
@@ -0,0 +1,324 @@
+# Parts of this script are based on Kitty's bash integration. Kitty is
+# distributed under GPLv3, so this file is also distributed under GPLv3.
+# The license header is reproduced below:
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# We need to be in interactive mode to proceed.
+if [[ "$-" != *i* ]]; then builtin return; fi
+
+# When automatic shell integration is active, we were started in POSIX
+# mode and need to manually recreate the bash startup sequence.
+if [ -n "$GHOSTTY_BASH_INJECT" ]; then
+ # Store a temporary copy of our startup flags and unset these global
+ # environment variables so we can safely handle reentrancy.
+ builtin declare __ghostty_bash_flags="$GHOSTTY_BASH_INJECT"
+ builtin unset ENV GHOSTTY_BASH_INJECT
+
+ # Restore an existing ENV that was replaced by the shell integration code.
+ if [[ -n "$GHOSTTY_BASH_ENV" ]]; then
+ builtin export ENV=$GHOSTTY_BASH_ENV
+ builtin unset GHOSTTY_BASH_ENV
+ fi
+
+ # Restore bash's default 'posix' behavior. Also reset 'inherit_errexit',
+ # which doesn't happen as part of the 'posix' reset.
+ builtin set +o posix
+ builtin shopt -u inherit_errexit 2>/dev/null
+
+ # Unexport HISTFILE if it was set by the shell integration code.
+ if [[ -n "$GHOSTTY_BASH_UNEXPORT_HISTFILE" ]]; then
+ builtin export -n HISTFILE
+ builtin unset GHOSTTY_BASH_UNEXPORT_HISTFILE
+ fi
+
+ # Manually source the startup files. See INVOCATION in bash(1) and
+ # run_startup_files() in shell.c in the Bash source code.
+ if builtin shopt -q login_shell; then
+ if [[ $__ghostty_bash_flags != *"--noprofile"* ]]; then
+ [ -r /etc/profile ] && builtin source "/etc/profile"
+ for __ghostty_rcfile in "$HOME/.bash_profile" "$HOME/.bash_login" "$HOME/.profile"; do
+ [ -r "$__ghostty_rcfile" ] && {
+ builtin source "$__ghostty_rcfile"
+ break
+ }
+ done
+ fi
+ else
+ if [[ $__ghostty_bash_flags != *"--norc"* ]]; then
+ # The location of the system bashrc is determined at bash build
+ # time via -DSYS_BASHRC and can therefore vary across distros:
+ # Arch, Debian, Ubuntu use /etc/bash.bashrc
+ # Fedora uses /etc/bashrc sourced from ~/.bashrc instead of SYS_BASHRC
+ # Void Linux uses /etc/bash/bashrc
+ # Nixos uses /etc/bashrc
+ for __ghostty_rcfile in /etc/bash.bashrc /etc/bash/bashrc /etc/bashrc; do
+ [ -r "$__ghostty_rcfile" ] && {
+ builtin source "$__ghostty_rcfile"
+ break
+ }
+ done
+ if [[ -z "$GHOSTTY_BASH_RCFILE" ]]; then GHOSTTY_BASH_RCFILE="$HOME/.bashrc"; fi
+ [ -r "$GHOSTTY_BASH_RCFILE" ] && builtin source "$GHOSTTY_BASH_RCFILE"
+ fi
+ fi
+
+ builtin unset __ghostty_rcfile
+ builtin unset __ghostty_bash_flags
+ 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
+ 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.
+ #
+ # This approach supports wrapping a `sudo` alias, but the alias definition
+ # must come _after_ this function is defined. Otherwise, the alias expansion
+ # will take precedence over this function, and it won't be wrapped.
+ function sudo {
+ builtin local sudo_has_sudoedit_flags="no"
+ for arg in "$@"; do
+ # Check if argument is '-e' or '--edit' (sudoedit flags)
+ if [[ "$arg" == "-e" || $arg == "--edit" ]]; then
+ sudo_has_sudoedit_flags="yes"
+ builtin break
+ fi
+ # Check if argument is neither an option nor a key-value pair
+ if [[ "$arg" != -* && "$arg" != *=* ]]; then
+ builtin break
+ fi
+ done
+ if [[ "$sudo_has_sudoedit_flags" == "yes" ]]; then
+ builtin command sudo "$@"
+ else
+ builtin command sudo --preserve-env=TERMINFO "$@"
+ fi
+ }
+fi
+
+# SSH Integration
+if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-* ]]; then
+ function ssh() {
+ builtin local ssh_term ssh_opts
+ ssh_term="xterm-256color"
+ ssh_opts=()
+
+ # Configure environment variables for remote session
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-env* ]]; then
+ ssh_opts+=(-o "SendEnv COLORTERM TERM_PROGRAM TERM_PROGRAM_VERSION")
+ fi
+
+ # Install terminfo on remote host if needed
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-terminfo* ]]; then
+ builtin local ssh_user ssh_hostname
+
+ while IFS=' ' read -r ssh_key ssh_value; do
+ case "$ssh_key" in
+ user) ssh_user="$ssh_value" ;;
+ hostname) ssh_hostname="$ssh_value" ;;
+ esac
+ [[ -n "$ssh_user" && -n "$ssh_hostname" ]] && break
+ done < <(builtin command ssh -G "$@" 2>/dev/null)
+
+ if [[ -n "$ssh_hostname" ]]; then
+ builtin local ssh_target="${ssh_user}@${ssh_hostname}"
+
+ # Check if terminfo is already cached
+ if "$GHOSTTY_BIN_DIR/ghostty" +ssh-cache --host="$ssh_target" >/dev/null 2>&1; then
+ ssh_term="xterm-ghostty"
+ elif builtin command -v infocmp >/dev/null 2>&1; then
+ builtin local ssh_terminfo ssh_cpath_dir ssh_cpath
+
+ ssh_terminfo=$(infocmp -0 -x xterm-ghostty 2>/dev/null)
+
+ if [[ -n "$ssh_terminfo" ]]; then
+ builtin echo "Setting up xterm-ghostty terminfo on $ssh_hostname..." >&2
+
+ ssh_cpath_dir=$(mktemp -d "/tmp/ghostty-ssh-$ssh_user.XXXXXX" 2>/dev/null) || ssh_cpath_dir="/tmp/ghostty-ssh-$ssh_user.$$"
+ ssh_cpath="$ssh_cpath_dir/socket"
+
+ if builtin echo "$ssh_terminfo" | builtin command ssh -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s "$@" '
+ infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
+ command -v tic >/dev/null 2>&1 || exit 1
+ mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
+ exit 1
+ ' 2>/dev/null; then
+ ssh_term="xterm-ghostty"
+ ssh_opts+=(-o "ControlPath=$ssh_cpath")
+
+ # Cache successful installation
+ "$GHOSTTY_BIN_DIR/ghostty" +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true
+ else
+ builtin echo "Warning: Failed to install terminfo." >&2
+ fi
+ else
+ builtin echo "Warning: Could not generate terminfo data." >&2
+ fi
+ else
+ builtin echo "Warning: ghostty command not available for cache management." >&2
+ fi
+ fi
+ fi
+
+ # Execute SSH with TERM environment variable
+ TERM="$ssh_term" COLORTERM=truecolor builtin command ssh "${ssh_opts[@]}" "$@"
+ }
+fi
+
+# This is set to 1 when we're executing a command so that we don't
+# send prompt marks multiple times.
+_ghostty_executing=""
+_ghostty_last_reported_cwd=""
+
+function __ghostty_precmd() {
+ local ret="$?"
+ if test "$_ghostty_executing" != "0"; then
+ _GHOSTTY_SAVE_PS1="$PS1"
+ _GHOSTTY_SAVE_PS2="$PS2"
+
+ # Use 133;P (not 133;A) inside PS1 to avoid fresh-line behavior on
+ # readline redraws (e.g., vi mode switches, Ctrl-L). The initial
+ # 133;A with fresh-line is emitted once via printf below.
+ PS1='\[\e]133;P;k=i\a\]'$PS1'\[\e]133;B\a\]'
+ PS2='\[\e]133;P;k=s\a\]'$PS2'\[\e]133;B\a\]'
+
+ # Bash doesn't redraw the leading lines in a multiline prompt so we mark
+ # the start of each line (after each newline) as a secondary prompt. This
+ # correctly handles multiline prompts by setting the first to primary and
+ # the subsequent lines to secondary.
+ #
+ # We only replace the \n prompt escape, not literal newlines ($'\n'),
+ # because literal newlines may appear inside $(...) command substitutions
+ # where inserting escape sequences would break shell syntax.
+ if [[ "$PS1" == *"\n"* ]]; then
+ PS1="${PS1//\\n/\\n$'\\[\\e]133;P;k=s\\a\\]'}"
+ fi
+
+ # Cursor
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *"cursor"* ]]; then
+ builtin local cursor=5 # blinking bar
+ [[ "$GHOSTTY_SHELL_FEATURES" == *"cursor:steady"* ]] && cursor=6 # steady bar
+
+ [[ "$PS1" != *"\[\e[${cursor} q\]"* ]] && PS1=$PS1"\[\e[${cursor} q\]"
+ [[ "$PS0" != *'\[\e[0 q\]'* ]] && PS0=$PS0'\[\e[0 q\]' # reset
+ fi
+
+ # Title (working directory)
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *"title"* ]]; then
+ PS1=$PS1'\[\e]2;\w\a\]'
+ fi
+ fi
+
+ if test "$_ghostty_executing" != ""; then
+ # End of current command. Report its status.
+ builtin printf "\e]133;D;%s;aid=%s\a" "$ret" "$BASHPID"
+ fi
+
+ # Fresh line and start of prompt. When ble.sh is active, emit 133;P instead
+ # of 133;A because ble.sh maintains its own cursor position tracking. 133;A's
+ # cursor movement (CR+LF when not at column 0) is invisible to ble.sh and
+ # desyncs its position state, causing display artifacts like duplicate
+ # prompts. See: https://github.com/akinomyoga/ble.sh/issues/684
+ if [[ -n "${BLE_VERSION-}" ]]; then
+ builtin printf "\e]133;P;k=i\a"
+ else
+ builtin printf "\e]133;A;redraw=last;cl=line;aid=%s\a" "$BASHPID"
+ fi
+
+ # unfortunately bash provides no hooks to detect cwd changes
+ # in particular this means cwd reporting will not happen for a
+ # command like cd /test && cat. PS0 is evaluated before cd is run.
+ if [[ "$_ghostty_last_reported_cwd" != "$PWD" ]]; then
+ _ghostty_last_reported_cwd="$PWD"
+ builtin printf "\e]7;kitty-shell-cwd://%s%s\a" "$HOSTNAME" "$PWD"
+ fi
+
+ _ghostty_executing=0
+}
+
+function __ghostty_preexec() {
+ builtin local cmd="$1"
+
+ PS1="$_GHOSTTY_SAVE_PS1"
+ PS2="$_GHOSTTY_SAVE_PS2"
+
+ # Title (current command)
+ if [[ -n $cmd && "$GHOSTTY_SHELL_FEATURES" == *"title"* ]]; then
+ builtin printf "\e]2;%s\a" "${cmd//[[:cntrl:]]/}"
+ fi
+
+ # End of input, start of output.
+ builtin printf "\e]133;C;\a"
+ _ghostty_executing=1
+}
+
+if (( BASH_VERSINFO[0] > 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 4) )); then
+ __ghostty_preexec_hook() {
+ builtin local cmd
+ cmd=$(LC_ALL=C HISTTIMEFORMAT='' builtin history 1)
+ cmd="${cmd#*[[:digit:]][* ] }" # remove leading history number
+ [[ -n "$cmd" ]] && __ghostty_preexec "$cmd"
+ }
+
+ __ghostty_hook() {
+ builtin local ret=$?
+ __ghostty_precmd "$ret"
+
+ # Append preexec hook to PS0 if not already present.
+ # Use function substitution in 5.3+, otherwise command substitution.
+ if [[ "$PS0" != *"__ghostty_preexec_hook"* ]]; then
+ if (( BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 3) )); then
+ # shellcheck disable=SC2016
+ PS0+='${ __ghostty_preexec_hook; }'
+ else
+ # shellcheck disable=SC2016
+ PS0+='$(__ghostty_preexec_hook >/dev/tty)'
+ fi
+ fi
+ }
+
+ # Append our hook to PROMPT_COMMAND, preserving its existing type.
+ #
+ # The 2>/dev/null suppresses "command not found" in subshells that inherit
+ # PROMPT_COMMAND without the function definition. This also silences any
+ # errors from inside __ghostty_hook itself, but those are all terminal escape
+ # sequences and non-actionable.
+ #
+ # shellcheck disable=SC2128,SC2178,SC2179
+ if [[ ";${PROMPT_COMMAND[*]:-};" != *";__ghostty_hook 2>/dev/null;"* ]]; then
+ if [[ -z "${PROMPT_COMMAND[*]}" ]]; then
+ if (( BASH_VERSINFO[0] > 5 || (BASH_VERSINFO[0] == 5 && BASH_VERSINFO[1] >= 1) )); then
+ PROMPT_COMMAND=("__ghostty_hook 2>/dev/null")
+ else
+ PROMPT_COMMAND="__ghostty_hook 2>/dev/null"
+ fi
+ elif [[ $(builtin declare -p PROMPT_COMMAND 2>/dev/null) == "declare -a "* ]]; then
+ PROMPT_COMMAND+=("__ghostty_hook 2>/dev/null")
+ else
+ [[ "${PROMPT_COMMAND}" =~ (\;[[:space:]]*|$'\n')$ ]] || PROMPT_COMMAND+=";"
+ PROMPT_COMMAND+="__ghostty_hook 2>/dev/null"
+ fi
+ fi
+else
+ builtin source "$(dirname -- "${BASH_SOURCE[0]}")/bash-preexec.sh"
+ preexec_functions+=(__ghostty_preexec)
+ precmd_functions+=(__ghostty_precmd)
+fi
diff --git a/vendor/libghostty-vt/src/shell-integration/elvish/lib/ghostty-integration.elv b/vendor/libghostty-vt/src/shell-integration/elvish/lib/ghostty-integration.elv
new file mode 100644
index 00000000..31ebf094
--- /dev/null
+++ b/vendor/libghostty-vt/src/shell-integration/elvish/lib/ghostty-integration.elv
@@ -0,0 +1,190 @@
+{
+ use platform
+ use str
+
+ # Clean up XDG_DATA_DIRS by removing GHOSTTY_SHELL_INTEGRATION_XDG_DIR
+ if (and (has-env GHOSTTY_SHELL_INTEGRATION_XDG_DIR) (has-env XDG_DATA_DIRS)) {
+ set-env XDG_DATA_DIRS (str:replace $E:GHOSTTY_SHELL_INTEGRATION_XDG_DIR":" "" $E:XDG_DATA_DIRS)
+ unset-env GHOSTTY_SHELL_INTEGRATION_XDG_DIR
+ }
+
+ # List of enabled shell integration features
+ var features = [(str:split ',' $E:GHOSTTY_SHELL_FEATURES)]
+
+ # State tracking for semantic prompt sequences
+ # Values: 'prompt-start', 'pre-exec', 'post-exec'
+ fn set-prompt-state {|new| set-env __ghostty_prompt_state $new }
+
+ fn mark-prompt-start {
+ if (not-eq $E:__ghostty_prompt_state 'prompt-start') {
+ printf "\e]133;D;aid="$pid"\a"
+ }
+ set-prompt-state 'prompt-start'
+ printf "\e]133;A;aid="$pid"\a"
+ }
+
+ fn mark-output-start {|_|
+ set-prompt-state 'pre-exec'
+ printf "\e]133;C\a"
+ }
+
+ fn mark-output-end {|cmd-info|
+ set-prompt-state 'post-exec'
+
+ var exit-status = 0
+
+ # in case of error: retrieve exit status,
+ # unless does not exist (= builtin function failure), then default to 1
+ if (not-eq $nil $cmd-info[error]) {
+ set exit-status = 1
+
+ if (has-key $cmd-info[error] reason) {
+ if (has-key $cmd-info[error][reason] exit-status) {
+ set exit-status = $cmd-info[error][reason][exit-status]
+ }
+ }
+ }
+
+ printf "\e]133;D;"$exit-status";aid="$pid"\a"
+ }
+
+ # NOTE: OSC 133;B (end of prompt, start of input) cannot be reliably
+ # implemented at the script level in Elvish. The prompt function's output is
+ # escaped, and writing to /dev/tty has timing issues because Elvish renders
+ # its prompts on a background thread. Full semantic prompt support requires a
+ # native implementation: https://github.com/elves/elvish/pull/1917
+
+ fn sudo-with-terminfo {|@args|
+ var sudoedit = $false
+ for arg $args {
+ if (str:has-prefix $arg --) {
+ if (eq $arg --edit) {
+ set sudoedit = $true
+ break
+ }
+ } elif (str:has-prefix $arg -) {
+ if (str:contains (str:trim-prefix $arg -) e) {
+ set sudoedit = $true
+ break
+ }
+ } elif (not (str:contains $arg =)) {
+ break
+ }
+ }
+
+ if (not $sudoedit) { set args = [ --preserve-env=TERMINFO $@args ] }
+ (external sudo) $@args
+ }
+
+ fn ssh-integration {|@args|
+ var ssh-term = "xterm-256color"
+ var ssh-opts = []
+
+ # Configure environment variables for remote session
+ if (has-value $features ssh-env) {
+ set ssh-opts = (conj $ssh-opts ^
+ -o "SendEnv COLORTERM TERM_PROGRAM TERM_PROGRAM_VERSION")
+ }
+
+ if (has-value $features ssh-terminfo) {
+ var ssh-user = ""
+ var ssh-hostname = ""
+
+ # Parse ssh config
+ for line [((external ssh) -G $@args)] {
+ var parts = [(str:fields $line)]
+ if (> (count $parts) 1) {
+ var ssh-key = $parts[0]
+ var ssh-value = $parts[1]
+ if (eq $ssh-key user) {
+ set ssh-user = $ssh-value
+ } elif (eq $ssh-key hostname) {
+ set ssh-hostname = $ssh-value
+ }
+ if (and (not-eq $ssh-user "") (not-eq $ssh-hostname "")) {
+ break
+ }
+ }
+ }
+
+ if (not-eq $ssh-hostname "") {
+ var ghostty = $E:GHOSTTY_BIN_DIR/"ghostty"
+ var ssh-target = $ssh-user"@"$ssh-hostname
+
+ # Check if terminfo is already cached
+ if (bool ?($ghostty +ssh-cache --host=$ssh-target)) {
+ set ssh-term = "xterm-ghostty"
+ } elif (has-external infocmp) {
+ var ssh-terminfo = ((external infocmp) -0 -x xterm-ghostty 2>/dev/null | slurp)
+
+ if (not-eq $ssh-terminfo "") {
+ echo "Setting up xterm-ghostty terminfo on "$ssh-hostname"..." >&2
+
+ use os
+ var ssh-cpath-dir = (os:temp-dir "ghostty-ssh-"$ssh-user".*")
+ var ssh-cpath = $ssh-cpath-dir"/socket"
+
+ if (bool ?(echo $ssh-terminfo | (external ssh) $@ssh-opts -o ControlMaster=yes -o ControlPath=$ssh-cpath -o ControlPersist=60s $@args '
+ infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
+ command -v tic >/dev/null 2>&1 || exit 1
+ mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
+ exit 1
+ ' 2>/dev/null)) {
+ set ssh-term = "xterm-ghostty"
+ set ssh-opts = (conj $ssh-opts -o ControlPath=$ssh-cpath)
+
+ # Cache successful installation
+ $ghostty +ssh-cache --add=$ssh-target >/dev/null
+ } else {
+ echo "Warning: Failed to install terminfo." >&2
+ }
+ } else {
+ echo "Warning: Could not generate terminfo data." >&2
+ }
+ } else {
+ echo "Warning: ghostty command not available for cache management." >&2
+ }
+ }
+ }
+
+ with [E:TERM = $ssh-term E:COLORTERM = truecolor] {
+ (external ssh) $@ssh-opts $@args
+ }
+ }
+
+ defer {
+ mark-prompt-start
+ }
+
+ set edit:before-readline = (conj $edit:before-readline $mark-prompt-start~)
+ set edit:after-readline = (conj $edit:after-readline $mark-output-start~)
+ set edit:after-command = (conj $edit:after-command $mark-output-end~)
+
+ if (str:contains $E:GHOSTTY_SHELL_FEATURES "cursor") {
+ var cursor = "5" # blinking bar
+ if (has-value $features cursor:steady) {
+ set cursor = "6" # steady bar
+ }
+
+ fn beam { printf "\e["$cursor" q" }
+ fn reset { printf "\e[0 q" }
+ set edit:before-readline = (conj $edit:before-readline $beam~)
+ set edit:after-readline = (conj $edit:after-readline {|_| reset })
+ }
+ if (and (has-value $features path) (has-env GHOSTTY_BIN_DIR)) {
+ if (not (has-value $paths $E:GHOSTTY_BIN_DIR)) {
+ 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~
+ }
+ if (and (str:contains $E:GHOSTTY_SHELL_FEATURES ssh-) (has-external ssh)) {
+ edit:add-var ssh~ $ssh-integration~
+ }
+
+ # Report changes to the current directory.
+ fn report-pwd { printf "\e]7;kitty-shell-cwd://%s%s\a" (platform:hostname) $pwd }
+ set after-chdir = (conj $after-chdir {|_| report-pwd })
+ report-pwd
+}
diff --git a/vendor/libghostty-vt/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish b/vendor/libghostty-vt/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish
new file mode 100644
index 00000000..f8bfe091
--- /dev/null
+++ b/vendor/libghostty-vt/src/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish
@@ -0,0 +1,245 @@
+# This shell script aims to be written in a way where it can't really fail
+# or all failure scenarios are handled, so that we never leave the shell in
+# a weird state. If you find a way to break this, please report a bug!
+
+function ghostty_restore_xdg_data_dir -d "restore the original XDG_DATA_DIR value"
+ # If we don't have our own data dir then we don't need to do anything.
+ if not set -q GHOSTTY_SHELL_INTEGRATION_XDG_DIR
+ return
+ end
+
+ # If the data dir isn't set at all then we don't need to do anything.
+ if not set -q XDG_DATA_DIRS
+ return
+ end
+
+ # We need to do this so that XDG_DATA_DIRS turns into an array.
+ set --function --path xdg_data_dirs "$XDG_DATA_DIRS"
+
+ # If our data dir is in the list then remove it.
+ if set --function index (contains --index "$GHOSTTY_SHELL_INTEGRATION_XDG_DIR" $xdg_data_dirs)
+ set --erase --function xdg_data_dirs[$index]
+ end
+
+ # Re-export our data dir
+ if set -q xdg_data_dirs[1]
+ set --global --export --unpath XDG_DATA_DIRS "$xdg_data_dirs"
+ else
+ set --erase --global XDG_DATA_DIRS
+ end
+
+ set --erase GHOSTTY_SHELL_INTEGRATION_XDG_DIR
+end
+
+function ghostty_exit -d "exit the shell integration setup"
+ functions -e ghostty_restore_xdg_data_dir
+ functions -e ghostty_exit
+ exit 0
+end
+
+# We always try to restore the XDG data dir
+ghostty_restore_xdg_data_dir
+
+# If we aren't interactive or we've already run, don't run.
+status --is-interactive || ghostty_exit
+
+# We do the full setup on the first prompt render. We do this so that other
+# shell integrations that setup the prompt and modify things are able to run
+# first. We want to run _last_.
+function __ghostty_setup --on-event fish_prompt -d "Setup ghostty integration"
+ functions -e __ghostty_setup
+
+ set --local features (string split , $GHOSTTY_SHELL_FEATURES)
+
+ # Parse the fish version for feature detection.
+ # Default to 0.0 if version is unavailable or malformed.
+ set -l fish_major 0
+ set -l fish_minor 0
+ if set -q version[1]
+ set -l fish_ver (string match -r '(\d+)\.(\d+)' -- $version[1])
+ if set -q fish_ver[2]; and test -n "$fish_ver[2]"
+ set fish_major "$fish_ver[2]"
+ end
+ if set -q fish_ver[3]; and test -n "$fish_ver[3]"
+ set fish_minor "$fish_ver[3]"
+ end
+ end
+
+ # Our OSC133A (prompt start) sequence. If we're using Fish >= 4.1
+ # then it supports click_events so we enable that.
+ set -g __ghostty_prompt_start_mark "\e]133;A\a"
+ if test "$fish_major" -gt 4; or test "$fish_major" -eq 4 -a "$fish_minor" -ge 1
+ set -g __ghostty_prompt_start_mark "\e]133;A;click_events=1\a"
+ end
+
+ if string match -q 'cursor*' -- $features
+ set -l cursor 5 # blinking bar
+ contains cursor:steady $features && set cursor 6 # steady bar
+
+ # Change the cursor to a beam on prompt.
+ function __ghostty_set_cursor_beam --on-event fish_prompt -V cursor -d "Set cursor shape"
+ if not functions -q fish_vi_cursor_handle
+ echo -en "\e[$cursor q"
+ end
+ end
+ function __ghostty_reset_cursor --on-event fish_preexec -d "Reset cursor shape"
+ if not functions -q fish_vi_cursor_handle
+ echo -en "\e[0 q"
+ end
+ end
+ end
+
+ # Add Ghostty binary to PATH if the path feature is enabled
+ if contains path $features; and test -n "$GHOSTTY_BIN_DIR"
+ fish_add_path --global --path --append "$GHOSTTY_BIN_DIR"
+ 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")
+ # Wrap `sudo` command to ensure Ghostty terminfo is preserved
+ function sudo -d "Wrap sudo to preserve terminfo"
+ set --function sudo_has_sudoedit_flags no
+ for arg in $argv
+ # Check if argument is '-e' or '--edit' (sudoedit flags)
+ if string match -q -- -e "$arg"; or string match -q -- --edit "$arg"
+ set --function sudo_has_sudoedit_flags yes
+ break
+ end
+ # Check if argument is neither an option nor a key-value pair
+ if not string match -r -q -- "^-" "$arg"; and not string match -r -q -- "=" "$arg"
+ break
+ end
+ end
+ if test "$sudo_has_sudoedit_flags" = yes
+ command sudo $argv
+ else
+ command sudo --preserve-env=TERMINFO $argv
+ end
+ end
+ end
+
+ # SSH Integration
+ set -l features (string split ',' -- "$GHOSTTY_SHELL_FEATURES")
+ if contains ssh-env $features; or contains ssh-terminfo $features
+ function ssh --wraps=ssh --description "SSH wrapper with Ghostty integration"
+ set -l features (string split ',' -- "$GHOSTTY_SHELL_FEATURES")
+ set -l ssh_term xterm-256color
+ set -l ssh_opts
+
+ # Configure environment variables for remote session
+ if contains ssh-env $features
+ set -a ssh_opts -o "SendEnv COLORTERM TERM_PROGRAM TERM_PROGRAM_VERSION"
+ end
+
+ # Install terminfo on remote host if needed
+ if contains ssh-terminfo $features
+ set -l ssh_user
+ set -l ssh_hostname
+
+ for line in (command ssh -G $argv 2>/dev/null)
+ set -l parts (string split ' ' -- $line)
+ if test (count $parts) -ge 2
+ switch $parts[1]
+ case user
+ set ssh_user $parts[2]
+ case hostname
+ set ssh_hostname $parts[2]
+ end
+ if test -n "$ssh_user"; and test -n "$ssh_hostname"
+ break
+ end
+ end
+ end
+
+ if test -n "$ssh_hostname"
+ set -l ssh_target "$ssh_user@$ssh_hostname"
+
+ # Check if terminfo is already cached
+ if test -x "$GHOSTTY_BIN_DIR/ghostty"; and "$GHOSTTY_BIN_DIR/ghostty" +ssh-cache --host="$ssh_target" >/dev/null 2>&1
+ set ssh_term xterm-ghostty
+ else if command -q infocmp
+ set -l ssh_terminfo
+ set -l ssh_cpath_dir
+ set -l ssh_cpath
+
+ set ssh_terminfo "$(infocmp -0 -x xterm-ghostty 2>/dev/null)"
+
+ if test -n "$ssh_terminfo"
+ echo "Setting up xterm-ghostty terminfo on $ssh_hostname..." >&2
+
+ set ssh_cpath_dir (mktemp -d "/tmp/ghostty-ssh-$ssh_user.XXXXXX" 2>/dev/null; or echo "/tmp/ghostty-ssh-$ssh_user."(random))
+ set ssh_cpath "$ssh_cpath_dir/socket"
+
+ if echo "$ssh_terminfo" | command ssh $ssh_opts -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s $argv '
+ infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
+ command -v tic >/dev/null 2>&1 || exit 1
+ mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
+ exit 1
+ ' 2>/dev/null
+ set ssh_term xterm-ghostty
+ set -a ssh_opts -o "ControlPath=$ssh_cpath"
+
+ # Cache successful installation
+ if test -x "$GHOSTTY_BIN_DIR/ghostty"
+ "$GHOSTTY_BIN_DIR/ghostty" +ssh-cache --add="$ssh_target" >/dev/null 2>&1; or true
+ end
+ else
+ echo "Warning: Failed to install terminfo." >&2
+ end
+ else
+ echo "Warning: Could not generate terminfo data." >&2
+ end
+ else
+ echo "Warning: ghostty command not available for cache management." >&2
+ end
+ end
+ end
+
+ # Execute SSH with TERM environment variable
+ TERM="$ssh_term" COLORTERM=truecolor command ssh $ssh_opts $argv
+ end
+ end
+
+ # Setup prompt marking
+ function __ghostty_mark_prompt_start --on-event fish_prompt --on-event fish_posterror
+ # If we never got the output end event, then we need to send it now.
+ if test "$__ghostty_prompt_state" != prompt-start
+ echo -en "\e]133;D\a"
+ end
+
+ set --global __ghostty_prompt_state prompt-start
+ echo -en $__ghostty_prompt_start_mark
+ end
+
+ function __ghostty_mark_output_start --on-event fish_preexec
+ set --global __ghostty_prompt_state pre-exec
+ echo -en "\e]133;C\a"
+ end
+
+ function __ghostty_mark_output_end --on-event fish_postexec
+ set --global __ghostty_prompt_state post-exec
+ echo -en "\e]133;D;$status\a"
+ end
+
+ # Report pwd. This is actually built-in to fish but only for terminals
+ # that match an allowlist and that isn't us.
+ function __update_cwd_osc --on-variable PWD -d 'Notify capable terminals when $PWD changes'
+ if status --is-command-substitution || set -q INSIDE_EMACS
+ return
+ end
+ printf \e\]7\;file://%s%s\a $hostname (string escape --style=url $PWD)
+ end
+
+ # Enable fish to handle reflow because Ghostty clears the prompt on resize.
+ set --global fish_handle_reflow 1
+
+ # Initial calls for first prompt
+ if string match -q 'cursor*' -- $features
+ __ghostty_set_cursor_beam
+ end
+ __ghostty_mark_prompt_start
+ __update_cwd_osc
+end
+
+ghostty_exit
diff --git a/vendor/libghostty-vt/src/shell-integration/nushell/vendor/autoload/ghostty.nu b/vendor/libghostty-vt/src/shell-integration/nushell/vendor/autoload/ghostty.nu
new file mode 100644
index 00000000..17970f51
--- /dev/null
+++ b/vendor/libghostty-vt/src/shell-integration/nushell/vendor/autoload/ghostty.nu
@@ -0,0 +1,110 @@
+# Ghostty shell integration
+export module ghostty {
+ def has_feature [feature: string] {
+ $feature in ($env.GHOSTTY_SHELL_FEATURES | default "" | split row ',')
+ }
+
+ # Wrap `ssh` with Ghostty TERMINFO support
+ export def --wrapped ssh [...args] {
+ mut ssh_env = {}
+ mut ssh_opts = []
+
+ # `ssh-env`: use xterm-256color and propagate COLORTERM/TERM_PROGRAM vars
+ if (has_feature "ssh-env") {
+ $ssh_env.TERM = "xterm-256color"
+ $ssh_env.COLORTERM = "truecolor"
+ $ssh_opts = [
+ "-o" "SendEnv COLORTERM TERM_PROGRAM TERM_PROGRAM_VERSION"
+ ]
+ }
+
+ # `ssh-terminfo`: auto-install xterm-ghostty terminfo on remote hosts
+ if (has_feature "ssh-terminfo") {
+ let ghostty = ($env.GHOSTTY_BIN_DIR? | default "") | path join "ghostty"
+
+ let ssh_cfg = ^ssh -G ...$args
+ | lines
+ | parse "{key} {value}"
+ | where key in ["user" "hostname"]
+ | select key value
+ | transpose -rd
+ | default {user: $env.USER hostname: "localhost"}
+ let ssh_id = $"($ssh_cfg.user)@($ssh_cfg.hostname)"
+
+ if (^$ghostty "+ssh-cache" $"--host=($ssh_id)" | complete | $in.exit_code == 0) {
+ $ssh_env.TERM = "xterm-ghostty"
+ } else {
+ $ssh_env.TERM = "xterm-256color"
+
+ let terminfo = try {
+ ^infocmp -0 -x xterm-ghostty
+ } catch {
+ print -e "infocmp failed, using xterm-256color"
+ }
+
+ if ($terminfo | is-not-empty) {
+ print $"Setting up xterm-ghostty terminfo on ($ssh_cfg.hostname)..."
+
+ let ctrl_path = (
+ mktemp -td $"ghostty-ssh-($ssh_cfg.user).XXXXXX"
+ | path join "socket"
+ )
+
+ let remote_args = $ssh_opts ++ [
+ "-o" "ControlMaster=yes"
+ "-o" $"ControlPath=($ctrl_path)"
+ "-o" "ControlPersist=60s"
+ ] ++ $args
+
+ $terminfo | ^ssh ...$remote_args '
+ infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
+ command -v tic >/dev/null 2>&1 || exit 1
+ mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
+ exit 1'
+ | complete
+ | if $in.exit_code == 0 {
+ ^$ghostty "+ssh-cache" $"--add=($ssh_id)" e>| print -e
+ $ssh_env.TERM = "xterm-ghostty"
+ $ssh_opts = ($ssh_opts ++ ["-o" $"ControlPath=($ctrl_path)"])
+ } else {
+ print -e "terminfo install failed, using xterm-256color"
+ }
+ }
+ }
+ }
+
+ let ssh_args = $ssh_opts ++ $args
+ with-env $ssh_env {
+ ^ssh ...$ssh_args
+ }
+ }
+
+ # Wrap `sudo` to preserve Ghostty's TERMINFO environment variable
+ export def --wrapped sudo [...args] {
+ mut sudo_args = $args
+
+ if (has_feature "sudo") {
+ # Extract just the sudo options (before the command)
+ let sudo_options = (
+ $args | take until {|arg|
+ not (($arg | str starts-with "-") or ($arg | str contains "="))
+ }
+ )
+
+ # Prepend TERMINFO preservation flag if not using sudoedit
+ if (not ("-e" in $sudo_options or "--edit" in $sudo_options)) {
+ $sudo_args = ($args | prepend "--preserve-env=TERMINFO")
+ }
+ }
+
+ ^sudo ...$sudo_args
+ }
+}
+
+# Clean up XDG_DATA_DIRS by removing GHOSTTY_SHELL_INTEGRATION_XDG_DIR
+if 'GHOSTTY_SHELL_INTEGRATION_XDG_DIR' in $env {
+ if 'XDG_DATA_DIRS' in $env {
+ $env.XDG_DATA_DIRS = ($env.XDG_DATA_DIRS | str replace $"($env.GHOSTTY_SHELL_INTEGRATION_XDG_DIR):" "")
+ }
+ hide-env GHOSTTY_SHELL_INTEGRATION_XDG_DIR
+}
diff --git a/vendor/libghostty-vt/src/shell-integration/zsh/.zshenv b/vendor/libghostty-vt/src/shell-integration/zsh/.zshenv
new file mode 100644
index 00000000..62dcf273
--- /dev/null
+++ b/vendor/libghostty-vt/src/shell-integration/zsh/.zshenv
@@ -0,0 +1,61 @@
+# Based on (started as) a copy of Kitty's zsh integration. Kitty is
+# distributed under GPLv3, so this file is also distributed under GPLv3.
+# The license header is reproduced below:
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This script is sourced automatically by zsh when ZDOTDIR is set to this
+# directory. It therefore assumes it's running within our shell integration
+# environment and should not be sourced manually (unlike ghostty-integration).
+#
+# This file can get sourced with aliases enabled. To avoid alias expansion
+# we quote everything that can be quoted. Some aliases will still break us
+# though.
+
+# Restore the original ZDOTDIR value if GHOSTTY_ZSH_ZDOTDIR is set.
+# Otherwise, unset the ZDOTDIR that was set during shell injection.
+if [[ -n "${GHOSTTY_ZSH_ZDOTDIR+X}" ]]; then
+ 'builtin' 'export' ZDOTDIR="$GHOSTTY_ZSH_ZDOTDIR"
+ 'builtin' 'unset' 'GHOSTTY_ZSH_ZDOTDIR'
+else
+ 'builtin' 'unset' 'ZDOTDIR'
+fi
+
+# Use try-always to have the right error code.
+{
+ # Zsh treats unset ZDOTDIR as if it was HOME. We do the same.
+ #
+ # Source the user's .zshenv before sourcing ghostty-integration because the
+ # former might set fpath and other things without which ghostty-integration
+ # won't work.
+ #
+ # Use typeset in case we are in a function with warn_create_global in
+ # effect. Unlikely but better safe than sorry.
+ 'builtin' 'typeset' _ghostty_file=${ZDOTDIR-$HOME}"/.zshenv"
+ # Zsh ignores unreadable rc files. We do the same.
+ # Zsh ignores rc files that are directories, and so does source.
+ [[ ! -r "$_ghostty_file" ]] || 'builtin' 'source' '--' "$_ghostty_file"
+} always {
+ if [[ -o 'interactive' ]]; then
+ # ${(%):-%x} is the path to the current file.
+ # On top of it we add :A:h to get the directory.
+ 'builtin' 'typeset' _ghostty_file="${${(%):-%x}:A:h}"/ghostty-integration
+ if [[ -r "$_ghostty_file" ]]; then
+ 'builtin' 'autoload' '-Uz' '--' "$_ghostty_file"
+ "${_ghostty_file:t}"
+ 'builtin' 'unfunction' '--' "${_ghostty_file:t}"
+ fi
+ fi
+ 'builtin' 'unset' '_ghostty_file'
+}
diff --git a/vendor/libghostty-vt/src/shell-integration/zsh/ghostty-integration b/vendor/libghostty-vt/src/shell-integration/zsh/ghostty-integration
new file mode 100644
index 00000000..76c5ce24
--- /dev/null
+++ b/vendor/libghostty-vt/src/shell-integration/zsh/ghostty-integration
@@ -0,0 +1,454 @@
+# vim:ft=zsh
+#
+# Based on (started as) a copy of Kitty's zsh integration. Kitty is
+# distributed under GPLv3, so this file is also distributed under GPLv3.
+# The license header is reproduced below:
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#
+# Enables integration between zsh and ghostty.
+#
+# This is an autoloadable function. It's invoked automatically in shells
+# directly spawned by Ghostty but not in any other shells. For example, running
+# `exec zsh`, `sudo -E zsh`, `tmux`, or plain `zsh` will create a shell where
+# ghostty-integration won't automatically run. Zsh users who want integration with
+# Ghostty in all shells should add the following lines to their .zshrc:
+#
+# if [[ -n $GHOSTTY_RESOURCES_DIR ]]; then
+# source "$GHOSTTY_RESOURCES_DIR"/shell-integration/zsh/ghostty-integration
+# fi
+#
+# Implementation note: We can assume that alias expansion is disabled in this
+# file, so no need to quote defensively. We still have to defensively prefix all
+# builtins with `builtin` to avoid accidentally invoking user-defined functions.
+# We avoid `function` reserved word as an additional defensive measure.
+
+# Note that updating options with `builtin emulate -L zsh` affects the global options
+# if it's called outside of a function. So nearly all code has to be in functions.
+_entrypoint() {
+ builtin emulate -L zsh -o no_warn_create_global -o no_aliases
+
+ [[ -o interactive ]] || builtin return 0 # non-interactive shell
+ (( ! $+_ghostty_state )) || builtin return 0 # already initialized
+
+ # We require zsh 5.1+ (released Sept 2015) for features like functions_source,
+ # introspection arrays, and array pattern substitution.
+ if ! { builtin autoload -- is-at-least 2>/dev/null && is-at-least 5.1; }; then
+ builtin echo "Zsh ${ZSH_VERSION} is too old for ghostty shell integration (5.1+ required)" >&2
+ builtin return 1
+ fi
+
+ # 0: no OSC 133 [AC] marks have been written yet.
+ # 1: the last written OSC 133 C has not been closed with D yet.
+ # 2: none of the above.
+ builtin typeset -gi _ghostty_state
+
+ # Attempt to create a writable file descriptor to the TTY so that we can print
+ # to the TTY later even when STDOUT is redirected. This code is fairly subtle.
+ #
+ # - It's tempting to do `[[ -t 1 ]] && exec {_ghostty_state}>&1` but we cannot do this
+ # because it'll create a file descriptor >= 10 without O_CLOEXEC. This file
+ # descriptor will leak to child processes.
+ # - If we do `exec {3}>&1`, the file descriptor won't leak to the child processes
+ # but it'll still leak if the current process is replaced with another. In
+ # addition, it'll break user code that relies on fd 3 being available.
+ # - Zsh doesn't expose dup3, which would have allowed us to copy STDOUT with
+ # O_CLOEXEC. The only way to create a file descriptor with O_CLOEXEC is via
+ # sysopen.
+ # - `zmodload zsh/system` and `sysopen -o cloexec -wu _ghostty_fd -- /dev/tty` can
+ # fail with an error message to STDERR (the latter can happen even if /dev/tty
+ # is writable), hence the redirection of STDERR. We do it for the whole block
+ # for performance reasons (redirections are slow).
+ # - We must open the file descriptor right here rather than in _ghostty_deferred_init
+ # because there are broken zsh plugins out there that run `exec {fd}< <(cmd)`
+ # and then close the file descriptor more than once while suppressing errors.
+ # This could end up closing our file descriptor if we opened it in
+ # _ghostty_deferred_init.
+ typeset -gi _ghostty_fd
+ {
+ builtin zmodload zsh/system && (( $+builtins[sysopen] )) && {
+ { [[ -w $TTY ]] && builtin sysopen -o cloexec -wu _ghostty_fd -- $TTY } ||
+ { [[ -w /dev/tty ]] && builtin sysopen -o cloexec -wu _ghostty_fd -- /dev/tty }
+ }
+ } 2>/dev/null || (( _ghostty_fd = 1 ))
+
+ # Defer initialization so that other zsh init files can be configure
+ # the integration.
+ builtin typeset -ag precmd_functions
+ precmd_functions+=(_ghostty_deferred_init)
+}
+
+_ghostty_deferred_init() {
+ builtin emulate -L zsh -o no_warn_create_global -o no_aliases
+
+ # Enable semantic markup with OSC 133.
+ _ghostty_precmd() {
+ builtin local -i cmd_status=$?
+ builtin emulate -L zsh -o no_warn_create_global -o no_aliases
+
+ # Don't write OSC 133 D when our precmd handler is invoked from zle.
+ # Some plugins do that to update prompt on cd.
+ if ! builtin zle; then
+ # This code works incorrectly in the presence of a precmd or chpwd
+ # hook that prints. For example, sindresorhus/pure prints an empty
+ # line on precmd and marlonrichert/zsh-snap prints $PWD on chpwd.
+ # We'll end up writing our OSC 133 D mark too late.
+ #
+ # Another failure mode is when the output of a command doesn't end
+ # with LF and prompst_sp is set (it is by default). In this case
+ # we'll incorrectly state that '%' from prompt_sp is a part of the
+ # command's output.
+ if (( _ghostty_state == 1 )); then
+ # The last written OSC 133 C has not been closed with D yet.
+ # Close it and supply status.
+ builtin print -nu $_ghostty_fd '\e]133;D;'$cmd_status'\a'
+ (( _ghostty_state = 2 ))
+ elif (( _ghostty_state == 2 )); then
+ # There might be an unclosed OSC 133 C. Close that.
+ builtin print -nu $_ghostty_fd '\e]133;D\a'
+ fi
+ fi
+
+ builtin local mark1=$'%{\e]133;A;cl=line\a%}'
+ if [[ -o prompt_percent ]]; then
+ builtin typeset -g precmd_functions
+ if [[ ${precmd_functions[-1]} == _ghostty_precmd ]]; then
+ # This is the best case for us: we can add our marks to PS1 and
+ # PS2. This way our marks will be printed whenever zsh
+ # redisplays prompt: on reset-prompt, on SIGWINCH, and on
+ # SIGCHLD if notify is set. Themes that update prompt
+ # asynchronously from a `zle -F` handler might still remove our
+ # marks. Oh well.
+
+ # Restore PS1/PS2 to their pre-mark state if nothing else has
+ # modified them since we last added marks. This avoids exposing
+ # PS1 with our marks to other hooks (which can break themes like
+ # Pure that use pattern matching to strip/rebuild the prompt).
+ # If PS1 was modified (by a theme, async update, etc.), we
+ # keep the modified version, prioritizing the theme's changes.
+ builtin local ps1_changed=0
+ if [[ -n ${_ghostty_saved_ps1+x} ]]; then
+ if [[ $PS1 == $_ghostty_marked_ps1 ]]; then
+ PS1=$_ghostty_saved_ps1
+ PS2=$_ghostty_saved_ps2
+ elif [[ $PS1 != $_ghostty_saved_ps1 ]]; then
+ ps1_changed=1
+ fi
+ fi
+
+ # Save the clean PS1/PS2 before we add marks.
+ _ghostty_saved_ps1=$PS1
+ _ghostty_saved_ps2=$PS2
+
+ # Add our marks. Since we always start from a clean PS1
+ # (either restored above or freshly set by a theme), we can
+ # unconditionally add mark1 and markB.
+ builtin local mark2=$'%{\e]133;P;k=s\a%}'
+ builtin local markB=$'%{\e]133;B\a%}'
+ # If PS1 ends with a bare '%', it combines with the '{'
+ # in markB to form a '%{' prompt escape, swallowing the
+ # marker and producing a visible '{'. Fix by doubling the
+ # trailing '%' so it becomes a literal '%%'.
+ [[ $PS1 == *[^%]% || $PS1 == % ]] && PS1=$PS1%
+ PS1=${mark1}${PS1}${markB}
+
+ # Handle multiline prompts by marking newline-separated
+ # continuation lines with k=s (mark2).
+ #
+ # We skip this when PS1 changed because injecting marks into
+ # newlines can break pattern matching in themes that
+ # strip/rebuild the prompt dynamically (e.g., Pure).
+ if (( ! ps1_changed )) && [[ $PS1 == *$'\n'* ]]; then
+ PS1=${PS1//$'\n'/$'\n'${mark2}}
+ fi
+
+ # PS2 mark is needed when clearing the prompt on resize
+ [[ $PS2 == *[^%]% || $PS2 == % ]] && PS2=$PS2%
+ PS2=${mark2}${PS2}${markB}
+
+ # Save the marked PS1 so we can detect modifications
+ # by other hooks in the next cycle.
+ _ghostty_marked_ps1=$PS1
+ (( _ghostty_state = 2 ))
+ else
+ # If our precmd hook is not the last, we cannot rely on prompt
+ # changes to stick, so we don't even try. At least we can move
+ # our hook to the end to have better luck next time. If there is
+ # another piece of code that wants to take this privileged
+ # position, this won't work well. We'll break them as much as
+ # they are breaking us.
+ precmd_functions=(${precmd_functions:#_ghostty_precmd} _ghostty_precmd)
+ # Plugins that invoke precmd hooks from zle do that before zle
+ # is trashed. This means that the cursor is in the middle of
+ # BUFFER and we cannot print our mark there. Prompt might
+ # already have a mark, so the following reset-prompt will write
+ # it. If it doesn't, there is nothing we can do.
+ if ! builtin zle; then
+ builtin print -rnu $_ghostty_fd -- $mark1[3,-3]
+ (( _ghostty_state = 2 ))
+ fi
+ fi
+ elif ! builtin zle; then
+ # Without prompt_percent we cannot patch prompt. Just print the
+ # mark, except when we are invoked from zle. In the latter case we
+ # cannot do anything.
+ builtin print -rnu $_ghostty_fd -- $mark1[3,-3]
+ (( _ghostty_state = 2 ))
+ fi
+ }
+
+ _ghostty_preexec() {
+ builtin emulate -L zsh -o no_warn_create_global -o no_aliases
+
+ # Restore the original PS1/PS2 if nothing else has modified them
+ # since our precmd added marks. This ensures other preexec hooks
+ # see a clean PS1 without our marks. If PS1 was modified (e.g.,
+ # by an async theme update), we leave it alone.
+ if [[ -n ${_ghostty_saved_ps1+x} && $PS1 == $_ghostty_marked_ps1 ]]; then
+ PS1=$_ghostty_saved_ps1
+ PS2=$_ghostty_saved_ps2
+ fi
+
+ # This will work incorrectly in the presence of a preexec hook that
+ # prints. For example, if MichaelAquilina/zsh-you-should-use installs
+ # its preexec hook before us, we'll incorrectly mark its output as
+ # belonging to the command (as if the user typed it into zle) rather
+ # than command output.
+ builtin print -nu $_ghostty_fd '\e]133;C\a'
+ (( _ghostty_state = 1 ))
+ }
+
+ # Enable reporting current working dir to terminal. Ghostty supports
+ # the kitty-shell-cwd format.
+ _ghostty_report_pwd() { builtin print -nu $_ghostty_fd '\e]7;kitty-shell-cwd://'"$HOST""$PWD"'\a'; }
+ chpwd_functions=(${chpwd_functions[@]} "_ghostty_report_pwd")
+ # An executed program could change cwd and report the changed cwd, so also report cwd at each new prompt
+ # as in this case chpwd_functions is insufficient. chpwd_functions is still needed for things like: cd x && something
+ functions[_ghostty_precmd]+="
+ _ghostty_report_pwd"
+ _ghostty_report_pwd
+
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *"title"* ]]; then
+ # Enable terminal title changes, formatted for user-friendly display.
+ functions[_ghostty_precmd]+="
+ builtin print -rnu $_ghostty_fd \$'\\e]2;'\"\${(%):-%(4~|…/%3~|%~)}\"\$'\\a'"
+ functions[_ghostty_preexec]+="
+ builtin print -rnu $_ghostty_fd \$'\\e]2;'\"\${1//[[:cntrl:]]}\"\$'\\a'"
+ fi
+
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *"cursor"* ]]; then
+ # Enable cursor shape changes depending on the current keymap.
+ # This implementation leaks blinking block cursor into external commands
+ # executed from zle. For example, users of fzf-based widgets may find
+ # themselves with a blinking block cursor within fzf.
+ _ghostty_zle_line_init _ghostty_zle_line_finish _ghostty_zle_keymap_select() {
+ builtin local steady=0
+ [[ "$GHOSTTY_SHELL_FEATURES" == *"cursor:steady"* ]] && steady=1
+ case ${KEYMAP-} in
+ vicmd|visual) builtin print -nu "$_ghostty_fd" "\e[$(( 1 + steady )) q" ;; # block
+ *) builtin print -nu "$_ghostty_fd" "\e[$(( 5 + steady )) q" ;; # bar
+ esac
+ }
+ # Restore the default shape before executing an external command
+ functions[_ghostty_preexec]+="
+ builtin print -rnu $_ghostty_fd \$'\\e[0 q'"
+ fi
+
+ # Emit semantic prompt markers at line-init if PS1 doesn't contain our
+ # marks. This ensures the terminal sees prompt markers even if another
+ # plugin (like zinit or oh-my-posh) regenerated PS1 after our precmd ran.
+ # We use 133;P instead of 133;A to avoid fresh-line behavior which would
+ # disrupt the display since the prompt has already been drawn. We also
+ # emit 133;B to mark the input area, which is needed for click-to-move.
+ (( $+functions[_ghostty_zle_line_init] )) || _ghostty_zle_line_init() { builtin true; }
+ functions[_ghostty_zle_line_init]="
+ if [[ \$PS1 != *$'%{\\e]133;A'* ]]; then
+ builtin print -nu \$_ghostty_fd '\\e]133;P;k=i\\a\\e]133;B\\a'
+ fi
+ "${functions[_ghostty_zle_line_init]}
+
+ # Add Ghostty binary to PATH if the path feature is enabled
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *"path"* ]] && [[ -n "$GHOSTTY_BIN_DIR" ]]; then
+ 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
+ function sudo() {
+ builtin local sudo_has_sudoedit_flags="no"
+ for arg in "$@"; do
+ # Check if argument is '-e' or '--edit' (sudoedit flags)
+ if [[ "$arg" == "-e" || $arg == "--edit" ]]; then
+ sudo_has_sudoedit_flags="yes"
+ builtin break
+ fi
+ # Check if argument is neither an option nor a key-value pair
+ if [[ "$arg" != -* && "$arg" != *=* ]]; then
+ builtin break
+ fi
+ done
+ if [[ "$sudo_has_sudoedit_flags" == "yes" ]]; then
+ builtin command sudo "$@";
+ else
+ builtin command sudo --preserve-env=TERMINFO "$@";
+ fi
+ }
+ fi
+
+ # SSH Integration
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-* ]]; then
+ function ssh() {
+ emulate -L zsh
+ setopt local_options no_glob_subst
+
+ local ssh_term ssh_opts
+ ssh_term="xterm-256color"
+ ssh_opts=()
+
+ # Configure environment variables for remote session
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-env* ]]; then
+ ssh_opts+=(-o "SendEnv COLORTERM TERM_PROGRAM TERM_PROGRAM_VERSION")
+ fi
+
+ # Install terminfo on remote host if needed
+ if [[ "$GHOSTTY_SHELL_FEATURES" == *ssh-terminfo* ]]; then
+ local ssh_user ssh_hostname
+
+ while IFS=' ' read -r ssh_key ssh_value; do
+ case "$ssh_key" in
+ user) ssh_user="$ssh_value" ;;
+ hostname) ssh_hostname="$ssh_value" ;;
+ esac
+ [[ -n "$ssh_user" && -n "$ssh_hostname" ]] && break
+ done < <(command ssh -G "$@" 2>/dev/null)
+
+ if [[ -n "$ssh_hostname" ]]; then
+ local ssh_target="${ssh_user}@${ssh_hostname}"
+
+ # Check if terminfo is already cached
+ if "$GHOSTTY_BIN_DIR/ghostty" +ssh-cache --host="$ssh_target" >/dev/null 2>&1; then
+ ssh_term="xterm-ghostty"
+ elif (( $+commands[infocmp] )); then
+ local ssh_terminfo ssh_cpath_dir ssh_cpath
+
+ ssh_terminfo=$(infocmp -0 -x xterm-ghostty 2>/dev/null)
+
+ if [[ -n "$ssh_terminfo" ]]; then
+ print "Setting up xterm-ghostty terminfo on $ssh_hostname..." >&2
+
+ ssh_cpath_dir=$(mktemp -d "/tmp/ghostty-ssh-$ssh_user.XXXXXX" 2>/dev/null) || ssh_cpath_dir="/tmp/ghostty-ssh-$ssh_user.$$"
+ ssh_cpath="$ssh_cpath_dir/socket"
+
+ if builtin print -r "$ssh_terminfo" | command ssh "${ssh_opts[@]}" -o ControlMaster=yes -o ControlPath="$ssh_cpath" -o ControlPersist=60s "$@" '
+ infocmp xterm-ghostty >/dev/null 2>&1 && exit 0
+ command -v tic >/dev/null 2>&1 || exit 1
+ mkdir -p ~/.terminfo 2>/dev/null && tic -x - 2>/dev/null && exit 0
+ exit 1
+ ' 2>/dev/null; then
+ ssh_term="xterm-ghostty"
+ ssh_opts+=(-o "ControlPath=$ssh_cpath")
+
+ # Cache successful installation
+ "$GHOSTTY_BIN_DIR/ghostty" +ssh-cache --add="$ssh_target" >/dev/null 2>&1 || true
+ else
+ print "Warning: Failed to install terminfo." >&2
+ fi
+ else
+ print "Warning: Could not generate terminfo data." >&2
+ fi
+ else
+ print "Warning: ghostty command not available for cache management." >&2
+ fi
+ fi
+ fi
+
+ # Execute SSH with TERM environment variable
+ TERM="$ssh_term" COLORTERM=truecolor command ssh "${ssh_opts[@]}" "$@"
+ }
+ fi
+
+ # Some zsh users manually run `source ~/.zshrc` in order to apply rc file
+ # changes to the current shell. This is a terrible practice that breaks many
+ # things, including our shell integration. For example, Oh My Zsh and Prezto
+ # (both very popular among zsh users) will remove zle-line-init and
+ # zle-line-finish hooks if .zshrc is manually sourced. Prezto will also remove
+ # zle-keymap-select.
+ #
+ # Another common (and much more robust) way to apply rc file changes to the
+ # current shell is `exec zsh`. This will remove our integration from the shell
+ # unless it's explicitly invoked from .zshrc. This is not an issue with
+ # `exec zsh` but rather with our implementation of automatic shell integration.
+
+ # In the ideal world we would use add-zle-hook-widget to hook zle-line-init
+ # and similar widget. This breaks user configs though, so we have do this
+ # horrible thing instead.
+ builtin local hook func widget orig_widget flag
+ for hook in line-init line-finish keymap-select; do
+ func=_ghostty_zle_${hook/-/_}
+ (( $+functions[$func] )) || builtin continue
+ widget=zle-$hook
+ if [[ $widgets[$widget] == user:azhw:* &&
+ $+functions[add-zle-hook-widget] -eq 1 ]]; then
+ # If the widget is already hooked by add-zle-hook-widget at the top
+ # level, add our hook at the end. We MUST do it this way. We cannot
+ # just wrap the widget ourselves in this case because it would
+ # trigger bugs in add-zle-hook-widget.
+ add-zle-hook-widget $hook $func
+ else
+ if (( $+widgets[$widget] )); then
+ # There is a widget but it's not from add-zle-hook-widget. We
+ # can rename the original widget, install our own and invoke
+ # the original when we are called.
+ #
+ # Note: The leading dot is to work around bugs in
+ # zsh-syntax-highlighting.
+ orig_widget=._ghostty_orig_$widget
+ builtin zle -A $widget $orig_widget
+ if [[ $widgets[$widget] == user:* ]]; then
+ # No -w here to preserve $WIDGET within the original widget.
+ flag=
+ else
+ flag=w
+ fi
+ functions[$func]+="
+ builtin zle $orig_widget -N$flag -- \"\$@\""
+ fi
+ builtin zle -N $widget $func
+ fi
+ done
+
+ if (( $+functions[_ghostty_preexec] )); then
+ builtin typeset -ag preexec_functions
+ preexec_functions+=(_ghostty_preexec)
+ fi
+
+ builtin typeset -ag precmd_functions
+ if (( $+functions[_ghostty_precmd] )); then
+ precmd_functions=(${precmd_functions:#_ghostty_deferred_init} _ghostty_precmd)
+ _ghostty_precmd
+ else
+ precmd_functions=(${precmd_functions:#_ghostty_deferred_init})
+ fi
+
+ # Unfunction _ghostty_deferred_init to save memory. Don't unfunction
+ # ghostty-integration though because decent public functions aren't supposed to
+ # to unfunction themselves when invoked. Unfunctioning is done by calling code.
+ builtin unfunction _ghostty_deferred_init
+}
+
+_entrypoint