# 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;A;k=s\a%}'
                builtin local markB=$'%{\e]133;B\a%}'
                PS1=${mark1}${PS1}${markB}

                # Handle multiline prompts by marking newline-separated
                # continuation lines with k=s (mark2). We skip the newline
                # immediately after mark1 to avoid introducing a double
                # newline due to OSC 133;A's fresh-line behavior.
                #
                # 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
                    if [[ $PS1 == ${mark1}$'\n'* ]]; then
                        builtin local rest=${PS1#${mark1}$'\n'}
                        if [[ $rest == *$'\n'* ]]; then
                            PS1=${mark1}$'\n'${rest//$'\n'/$'\n'${mark2}}
                        fi
                    else
                        PS1=${PS1//$'\n'/$'\n'${mark2}}
                    fi
                fi

                # PS2 mark is needed when clearing the prompt on resize
                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
    }

    # Wortel: true if the command line launches a long-lived interactive
    # program (agent CLI, editor, pager, REPL, system monitor, TUI) that sits
    # at its own prompt instead of returning to the shell. For those the
    # synthetic busy spinner (OSC 9;4;3 in preexec) would never clear until you
    # quit — no OSC 133;D fires — so we skip it and let the program drive its
    # own progress if it reports any. Batch commands (builds, tests) still spin.
    _ghostty_wortel_interactive() {
        builtin emulate -L zsh -o no_warn_create_global -o no_aliases
        builtin local -a words
        words=(${(z)1})
        builtin local -i i=1 n=$#words
        builtin local w cmd=
        # Skip leading env assignments (FOO=bar) and command wrappers to find
        # the program actually being launched.
        while (( i <= n )); do
            w=$words[i]
            case $w in
                *=*)                                              (( i++ )); continue ;;
                sudo|doas|command|builtin|exec|nohup|nice|stdbuf|setsid|env)
                                                                  (( i++ )); continue ;;
                npx|bunx|pnpx|uvx|pipx)                           (( i++ )); continue ;;
                npm|pnpm|yarn|bun|deno)
                    # Package runners: in `pnpm dlx codex` or `bun x pi` the
                    # program is the word after the subcommand. Only skip when
                    # the subcommand is actually there — a bare `bun` is a REPL,
                    # and it must keep reaching the dual-use check below.
                    case $words[i+1] in
                        x|dlx|exec|run)                           (( i += 2 )); continue ;;
                    esac
                    cmd=$w; break ;;
                -*)                                               (( i++ )); continue ;;
                *)                                                cmd=$w; break ;;
            esac
        done
        [[ -z $cmd ]] && return 1
        cmd=${cmd:t}
        case $cmd in
            claude|pi|aider|codex|cursor-agent|gemini|opencode|goose|cline|ollama|llm|crush|amp) return 0 ;;
            vi|vim|nvim|vis|nano|emacs|emacsclient|helix|hx|kak|micro|ne|joe) return 0 ;;
            less|more|most|moar|man|bat|w3m|lynx) return 0 ;;
            top|htop|btop|btm|glances|gtop|nvtop|s-tui) return 0 ;;
            ssh|mosh|telnet|tmux|screen|zellij|dtach|abduco) return 0 ;;
            watch|journalctl|dmesg) return 0 ;;
            fzf|sk|gitui|lazygit|lazydocker|k9s|tig|ranger|nnn|yazi|lf|vifm|mc|broot|xplr) return 0 ;;
            gdb|lldb|pdb) return 0 ;;
            psql|mysql|mariadb|sqlite3|litecli|pgcli|mycli|redis-cli|mongosh|mongo|usql|duckdb|clickhouse-client|cqlsh) return 0 ;;
            python|python2|python3|ipython|bpython|ptpython|node|nodejs|bun|deno|irb|pry|php|lua|luajit|julia|R|ghci|iex|erl|scala|clj|clojure)
                # Dual-use: a bare REPL is interactive, but `python script.py`,
                # `node app.js`, `python -m mod` and `-c ...` are batch jobs that
                # should keep the spinner. Interactive only with no such arg.
                builtin local a
                for a in ${words[i+1,-1]}; do
                    case $a in
                        -i|-q|-u|-B|-E|-O|-OO|-s|-S|-I|--) continue ;;
                        -c|-m)             return 1 ;;
                        *[\<\>\|\&\;]*)    return 0 ;;
                        -*)                continue ;;
                        *)                 return 1 ;;
                    esac
                done
                return 0 ;;
        esac
        # The leading word is not a program we know. It may well be a wrapper no
        # list can ever enumerate — `devx pi`, a repo's own `./run claude`, a
        # company shim — so look just past it: if one of the next couple of bare
        # words names an agent, believe that over the wrapper.
        #
        # Only two words, and flags skipped, to keep it from reading an agent's
        # name out of an unrelated argument. The asymmetry is deliberate: a
        # false positive costs a missing spinner on one command, while a false
        # negative leaves a spinner turning for the entire session — which is
        # the failure people actually report.
        # (Output discarded here — this caller only wants the verdict. Letting
        # it through would print the agent's name into the user's terminal at
        # every command.)
        _ghostty_wortel_wrapped_agent $cmd ${words[i+1,-1]} >/dev/null && return 0
        return 1
    }

    # Wortel: given the leading command and the words after it, echo the agent it
    # wraps — else nothing. Shared by the interactive check and the marker so a
    # wrapper-launched agent is both spinner-exempt and resumable.
    #
    # Guarded on both ends. Tools whose arguments are patterns or paths are
    # excluded outright, because `rg claude src` and `git log claude` are
    # ordinary commands that must keep their spinner, and no syntax
    # distinguishes a wrapper's subcommand from a search term. Past that, only
    # the first two bare words are read, so an agent's name buried deep in an
    # argument list is not mistaken for the program being launched.
    _ghostty_wortel_wrapped_agent() {
        builtin emulate -L zsh -o no_warn_create_global -o no_aliases
        builtin local lead=${1:t} w
        builtin shift
        case $lead in
            git|hg|svn|jj|grep|rg|ag|ack|fd|find|ls|cat|sed|awk|jq|yq|xargs|echo|printf) return 1 ;;
            cp|mv|rm|ln|mkdir|touch|tar|zip|unzip|open|chmod|chown|stat|file|du|df) return 1 ;;
            curl|wget|scp|rsync|diff|patch|wc|sort|uniq|head|tail|tr|cut|comm|tee) return 1 ;;
            make|cargo|go|rustc|swift|swiftc|xcodebuild|gradle|mvn|pip|pip3|brew|docker|kubectl) return 1 ;;
            which|type|whereis|whatis|history|kill|pkill|pgrep|ps|test) return 1 ;;
        esac
        builtin local -i looked=0
        for w in "$@"; do
            case $w in
                -*|*=*) continue ;;
            esac
            (( looked++ ))
            case ${w:t} in
                claude|pi|aider|codex|cursor-agent|gemini|opencode|goose|cline|ollama|llm|crush|amp)
                    builtin print -r -- ${w:t}; return 0 ;;
            esac
            (( looked >= 2 )) && break
        done
        return 1
    }

    # Wortel: echo the agent-CLI basename if the command line launches one, else
    # nothing. Same launcher/env-assignment skipping as _ghostty_wortel_interactive,
    # but matches only the resumable agent subset. Wortel records this per session
    # (keyed by WORTEL_SESSION_ID) so a relaunch can resume the agent.
    _ghostty_wortel_agent() {
        builtin emulate -L zsh -o no_warn_create_global -o no_aliases
        builtin local -a words
        words=(${(z)1})
        builtin local -i i=1 n=$#words
        builtin local w cmd=
        while (( i <= n )); do
            w=$words[i]
            case $w in
                *=*)                                              (( i++ )); continue ;;
                sudo|doas|command|builtin|exec|nohup|nice|stdbuf|setsid|env)
                                                                  (( i++ )); continue ;;
                npx|bunx|pnpx|uvx|pipx)                           (( i++ )); continue ;;
                npm|pnpm|yarn|bun|deno)
                    # Package runners: in `pnpm dlx codex` or `bun x pi` the
                    # program is the word after the subcommand. Only skip when
                    # the subcommand is actually there — a bare `bun` is a REPL,
                    # and it must keep reaching the dual-use check below.
                    case $words[i+1] in
                        x|dlx|exec|run)                           (( i += 2 )); continue ;;
                    esac
                    cmd=$w; break ;;
                -*)                                               (( i++ )); continue ;;
                *)                                                cmd=$w; break ;;
            esac
        done
        [[ -z $cmd ]] && return
        cmd=${cmd:t}
        case $cmd in
            claude|pi|aider|codex|cursor-agent|gemini|opencode|goose|cline|ollama|llm|crush|amp)
                builtin print -r -- $cmd; return ;;
        esac
        # Wrapper form (`devx pi`): record the agent, not the wrapper, so the
        # session is resumable and the app knows which agent is running.
        _ghostty_wortel_wrapped_agent $cmd ${words[i+1,-1]}
    }

    # Wortel: echo the model the command line selects (--model/-m/--models value),
    # else nothing. Best-effort, for the sidebar badge and to record what an agent
    # was using. Only consulted for commands _ghostty_wortel_agent already matched.
    _ghostty_wortel_model() {
        builtin emulate -L zsh -o no_warn_create_global -o no_aliases
        builtin local -a words
        words=(${(Q)${(z)1}})
        builtin local -i i=1 n=$#words
        builtin local w
        while (( i <= n )); do
            w=$words[i]
            case $w in
                --model=*|--models=*|-m=*)  builtin print -r -- ${w#*=}; return ;;
                --model|--models|-m)        (( i++ )); [[ -n $words[i] ]] && builtin print -r -- $words[i]; return ;;
            esac
            (( i++ ))
        done
    }

    # Wortel: directory shared with the app (AgentResumeStore) for agent markers.
    _ghostty_wortel_agent_dir="$HOME/Library/Application Support/Wortel/agents"

    # Wortel: clear this session's agent marker. Runs at every prompt (registered
    # into precmd below), so a marker only survives while an agent is actually
    # running — which is precisely when no prompt fires.
    _ghostty_wortel_clear_agent() {
        [[ -n $WORTEL_SESSION_ID ]] || return
        command rm -f "$_ghostty_wortel_agent_dir/$WORTEL_SESSION_ID" 2>/dev/null
    }

    _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'
        # Wortel: mark the session busy for the sidebar spinner. OSC 9;4;3 is
        # ConEmu's "indeterminate progress"; Ghostty forwards it as a progress
        # report. It clears automatically at the next prompt (OSC 133;D) — but
        # long-lived interactive programs never return to the prompt, so for
        # those we skip it (see _ghostty_wortel_interactive) to avoid a spinner
        # that spins for the whole session.
        if ! _ghostty_wortel_interactive "$1"; then
            builtin print -nu $_ghostty_fd '\e]9;4;3;\a'
        fi
        # Wortel: record a launched agent CLI so a relaunch can resume it. Keyed
        # by the surface's session id; cleared at the next prompt. A marker that
        # outlives the app means the agent was still running at quit.
        if [[ -n $WORTEL_SESSION_ID ]]; then
            builtin local _wortel_agent="$(_ghostty_wortel_agent "$1")"
            if [[ -n $_wortel_agent ]]; then
                # key=value marker read by AgentResumeStore: the agent, the model
                # it was launched with (if any), the cwd (to resume the right
                # project), and the exact command (one line) as a resume fallback.
                command mkdir -p "$_ghostty_wortel_agent_dir" 2>/dev/null
                {
                    builtin print -r -- "agent=$_wortel_agent"
                    builtin print -r -- "model=$(_ghostty_wortel_model "$1")"
                    builtin print -r -- "cwd=$PWD"
                    builtin print -r -- "argv=${1//$'\n'/ }"
                    # The shell's own pid. The agent runs as a descendant of it,
                    # so the app can ask the process table whether it is *really*
                    # still running rather than trusting this file to be current.
                    builtin print -r -- "shellpid=$$"
                    # When the agent started, so the app can tell which transcript
                    # the agent created (and so which conversation it is in).
                    builtin print -r -- "startedat=$(command date +%s)"
                } > "$_ghostty_wortel_agent_dir/$WORTEL_SESSION_ID" 2>/dev/null
            fi
        fi
        (( _ghostty_state = 1 ))
    }

    # Enable reporting current working dir to terminal. Ghostty supports
    # the kitty-shell-cwd format.
    # Wortel: "localhost", not "$HOST". libghostty accepts an OSC 7 only when
    # its host is "localhost" or an exact gethostname() match — anything else,
    # including a missing host, is treated as a remote path and dropped ("OSC 7
    # uri must contain a hostname" / "OSC 7 host () must be local"). $HOST
    # breaks that two ways:
    # it's just a parameter, so an environment that clears it (shadowenv does)
    # emits a hostless URI; and it's captured at shell start, so it goes stale
    # the moment macOS renames the machine mid-session — routine on a network
    # where mDNS hands you the "-2" suffix after a name collision. The shell
    # this script runs in is always local, so localhost is true by construction
    # and immune to both. A remote shell over ssh reports its own hostname and
    # is still correctly rejected: that integration is the remote host's copy,
    # not this one.
    _ghostty_report_pwd() { builtin print -nu $_ghostty_fd '\e]7;kitty-shell-cwd://localhost'"$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
    # Wortel: clear the agent marker at every prompt (see _ghostty_wortel_clear_agent).
    functions[_ghostty_precmd]+="
        _ghostty_wortel_clear_agent"

    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]}

    # WORTEL: put the bundled `wortel` command on PATH.
    #
    # This replaces upstream's block, which appended $GHOSTTY_BIN_DIR —
    # Contents/MacOS — so its own `ghostty` command would work. Wortel ships no
    # binary there. The only thing that directory holds is the app's own
    # executable, named `Wortel`, and the disk is case-insensitive: `wortel`
    # matched it, so typing our command launched a SECOND COPY OF THE APP,
    # which then failed to bind the CLI socket the first copy was holding.
    #
    # Turning it off in wortel.conf does not work, which is why the block is
    # deleted rather than disabled: the guard is `== *"path"*`, and a feature
    # list containing `no-path` matches that just as happily as one containing
    # `path`. (So does `no-cursor` against `*"cursor"*`. Worth knowing before
    # trusting any negation here.)
    #
    # Prepended, not appended: the command that ships with *this* app is the one
    # that speaks this app's protocol, and it should win over an older copy
    # someone symlinked onto their PATH. WORTEL_BIN_DIR is exported per surface
    # by GhosttyRuntime — a variable of our own rather than an override of PATH,
    # because libghostty's env_vars can only add.
    if [[ -n "$WORTEL_BIN_DIR" ]] && [[ ":$PATH:" != *":$WORTEL_BIN_DIR:"* ]]; then
      builtin export PATH="$WORTEL_BIN_DIR:$PATH"
    fi

    # And take Contents/MacOS back off, because the core puts it there whatever
    # the `path` feature says — verified from inside a session: the feature list
    # arrives as "ssh-env,ssh-terminfo,title" and the directory is on PATH
    # anyway.
    #
    # Nothing in it is meant to be run by name. The only file is the app's own
    # executable, `Wortel`, which a case-insensitive disk hands over for
    # `wortel` — so leaving it means any accident of ordering launches a SECOND
    # COPY OF THE APP rather than failing cleanly. Being first is not a strong
    # enough guarantee for a failure that severe.
    if [[ -n "$GHOSTTY_BIN_DIR" ]]; then
      PATH=":$PATH:"
      PATH="${PATH//:$GHOSTTY_BIN_DIR:/:}"
      PATH="${PATH#:}"
      PATH="${PATH%:}"
      builtin export PATH
    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 "SetEnv COLORTERM=truecolor")
          ssh_opts+=(-o "SendEnv 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" 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
