56 Commits

Author SHA1 Message Date
Jason Rayne
f95476b181 refactor: apply maintainer feedback to SSH integration scripts across all shells
- Update Bash script (baseline): Simplify cache checking logic, clarify
"xterm-ghostty terminfo" message, remove unnecessary ssh_opts from
terminfo installation, remove extra success message
- Align ZSH/Fish/Elvish with updated Bash: Remove extra success
messages, adopt simplified cache checking, standardize setup messages
- Apply Elvish improvements: Remove unnecessary try/catch blocks, use
idiomatic error handling patterns
- Apply Fish improvements: Replace string pattern matching with
efficient `contains` checks on split features list
2025-07-08 10:45:42 -07:00
Jason Rayne
f279937377 refactor: simplify terminfo handling and remove base64 dependency
- Default ssh_term to xterm-256color to eliminate fallback assignments
- Remove base64 and replace infocmp -Q2 with standard -0 -x options for
compatibility
- Use process substitution instead of intermediate ssh_config variable
- Always set TERM explicitly since ssh_term is always defined
2025-07-07 11:33:26 -07:00
Jason Rayne
c3b14dff71 refactor: simplify SSH terminfo and environment handling
- Simplify feature detection to use single wildcard check
- Replace ssh_env array with simple ssh_term string variable
- Use TERM environment prefix instead of save/restore pattern
- Remove unnecessary backgrounded subshell for cache operations
2025-07-07 10:00:56 -07:00
Jason Rayne
08db61e27e refactor: simplify SSH environment variable handling
- Remove complex ssh_exported_vars tracking and local environment
modification in favor of trusting Ghostty's local environment
- Replace regex patterns with glob-based feature detection for better
performance
- Fix local variable declaration consistency throughout
- Streamline logic while maintaining all functionality
2025-07-07 09:06:15 -07:00
Jason Rayne
2d2df4b99f Merge branch 'main' into ssh-integration 2025-07-05 13:34:47 -07:00
Jason Rayne
a22074a85c fix: optimize SSH integration and improve error handling
- Replace dual-loop SSH config parsing with efficient single-pass case
statement
- Remove overly cautious timeout logic from cache checks for simplicity
- Add base64 availability check with xterm-256color fallback when
missing
- Include hostname in terminfo setup messages for better UX
- Maintain SendEnv/SetEnv dual approach for maximum OpenSSH
compatibility (relying on SetEnv alone seems to drop some vars during my
tests, despite them being explicitly included in AcceptEnv on the remote
host)
2025-07-05 13:24:59 -07:00
Jon Parise
b8931dd1db bash: stop using PS0 for the 'cursor' feature
Our use of PS0 (which bash runs before command execution) was causing
raw command sequences to be printed between multiple commands in a
sequence.

    $ alias garbage='echo start
    > echo end'

    $ garbage
    start
    �\���dend

I wasn't able to definitely track down all of the reasons for why this
only happens in the command sequence case, but I suspect it's related to
the way that __ghostty_preexec runs from within the bash DEBUG trap (by
way of bash-preexec).

This problem occurs when PS0 is set to _any_ string (even "") inside of
__ghostty_preexec, which also rules out most/any Ghostty-specific code.
PS1 and PS2 appear to be safe to (re)set in this context.

Fortunately, we can avoid using PS0 entirely by instead printing the
cursor reset escape sequence directly from __ghostty_precmd because it
also runs just before command execution.
2025-07-04 20:06:06 -04:00
Jason Rayne
75c703071a feat(ssh): rewrite SSH cache system in native Zig
- Eliminates standalone bash dependency
- Consolidates `+list-ssh-cache` and `+clear-ssh-cache` actions into
single `+ssh-cache` action with args
- Structured cache format with timestamps and expiration support
- Memory-safe entry handling with proper file locking
- Comprehensive hostname validation (IPv4/IPv6/domains)
- Atomic updates via temp file + rename
- Updated shell integrations for improved cross-platform support and
reliability
- Cache operations are now unit-testable
2025-07-03 20:11:45 -07:00
Jason Rayne
076f742dd4 fix: replace non-existent GHOSTTY_VERSION with TERM_PROGRAM_VERSION in shell integration
GHOSTTY_VERSION was mistakenly referenced but is never set. Use
TERM_PROGRAM_VERSION which is actually provided by Exec.zig from
build_config.version_string.
2025-06-25 17:50:15 -07:00
Jason Rayne
0565ed3954 refactor: replace ghostty wrapper with proper CLI actions for terminfo cache management
- Add +list-ssh-cache and +clear-ssh-cache CLI actions
- Remove ghostty() wrapper functions from all shell integrations
- Improve variable naming in shell scripts for readability

Addresses @00-kat's feedback about CLI discoverability and naming
consistency. The new CLI actions follow established Ghostty patterns
and are discoverable via `ghostty --help`, while maintaining clean
separation of concerns between shell logic and cache management.
2025-06-25 15:46:18 -07:00
Jason Rayne
8a2fa6485e refactor: extract SSH cache functionality to shared script
Addresses feedback about separation of concerns in shell integration
scripts.

Extracts host caching logic to
`src/shell-integration/shared/ghostty-ssh-cache` and updates all four
shell integrations to use the shared script. The `shared/` subdirectory
preserves the existing organizational pattern where all shell-specific
code lives in subdirectories. This cleanly separates SSH transport logic
from cache management while reducing code duplication by ~25%.

All existing SSH integration behavior remains identical.
2025-06-25 15:46:18 -07:00
Jason Rayne
81641e56b1 ssh-integration: replace levels with flags, optimize implementation
Rewrote shell functions to support the two new flags for
shell-integration-features:
- ssh-env: TERM compatibility + best effort environment variable
propagation (anything beyond TERM will depend on what the remote host
allows)
- ssh-terminfo: automatic terminfo installation with control socket
orchestration
- Flags work independently or combined

Implementation optimizations:
- ~65% code reduction through unified execution path
- Eliminated GHOSTTY_SSH_INTEGRATION environment variable system
- Replaced complex function dispatch with direct flag detection
- Consolidated 4 cache helper functions into single _ghst_cache()
utility
- Simplified control socket management (removed multi-step
orchestration)
- Subsequent connections to cached hosts are now directly executed and
more reliable

New additions:
- If ssh-terminfo is enabled, ghostty will be wrapped to provide users
with convenient commands to invoke either of the two utility functions:
`ghostty ssh-cache-list` and `ghostty ssh-cache-clear`
2025-06-25 15:46:18 -07:00
Jason Rayne
ddd3da487e fix: update cache file location 2025-06-25 15:46:18 -07:00
Jason Rayne
30683979bc fix: catch up to current state 2025-06-25 15:46:18 -07:00
Jason Rayne
f206e76841 ssh-integration: improve host caching, new method for "full" integration
Need a sanity check on this new approach for "full" to help determine if
it's worth additional iteration/refinement.

It solves the double auth issue, successfully propagates env vars, and
avoids output noise for connections that happen after terminfo is
installed. The only issue I don't have time to fix tonight is the fact
that it drops the MOTD for cached (re)connections.
2025-06-25 15:46:18 -07:00
Jason Rayne
4cebee5c8e fix: add client-side caching to eliminate redundant terminfo installations
- Cache known hosts with terminfo in
$GHOSTTY_RESOURCES_DIR/terminfo_hosts
- Skip installation step for cached hosts (single connection instead of
two)
- Use secure file permissions (600) and atomic writes
- Extract SSH target safely from command arguments
- Maintains full functionality while improving user experience on
repeated connections
2025-06-25 15:46:18 -07:00
Jason Rayne
b6bb9abfbc fix: address comprehensive shell integration code review issues
- Fix elvish function name mismatch and use conj for list operations
- Simplify terminfo installation command per ghostty docs (tic -x -)
- Fix conditional structure to ensure error messages always print
- Remove redundant checks and optimize array initialization
- Use consistent patterns across bash, fish, elvish, and zsh
implementations
2025-06-25 15:46:18 -07:00
Jason Rayne
995fb09813 fix: add builtin prefix for safety and consistency 2025-06-25 15:46:18 -07:00
Jason Rayne
2ddcf2fffe fix: remove resources_dir var, add builtin prefix for consistency 2025-06-25 15:46:18 -07:00
Jason Rayne
2e9a0e92db fix: clean up SSH environment variable propagation 2025-06-25 15:46:18 -07:00
Jason Rayne
c70643404c bash: revert all formatting changes
Keeps only functional additions for SSH integration wrapper,
preserving original line breaks and indentation to minimize
diff noise per maintainer feedback.
2025-06-25 15:46:18 -07:00
Jason Rayne
842ced9212 bash: preserve mixed indentation in SSH integration changes
Preserves existing mixed indentation in ghostty.bash to minimize
diff noise per maintainer feedback.
2025-06-25 15:46:18 -07:00
Jason Rayne
2babdb458f refactor: simplify ssh integration environment variable checks 2025-06-25 15:46:18 -07:00
Jason Rayne
8f93d8fe03 fix: use kebab-case for ssh-integration enum values 2025-06-25 15:46:18 -07:00
Jason Rayne
142e07c502 feat: add SSH integration wrapper for shell integration
- Implements opt-in SSH wrapper following sudo pattern
- Supports term_only, basic, and full integration levels
- Fixes xterm-ghostty TERM compatibility on remote systems
- Propagates shell integration environment variables
- Allows for automatic installation of terminfo if desired
- Addresses GitHub discussions #5892 and #4156
2025-06-25 15:43:32 -07:00
Jon Parise
b629f3337a bash: remove dependency on $GHOSTTY_RESOURCES_DIR
We were depending on $GHOSTTY_RESOURCES_DIR for two reasons:

1. To locate our script-adjacent bash-preexec.sh script
2. To restrict our script's execution to environments in which
   $GHOSTTY_RESOURCES_DIR is available (i.e. Ghostty-only shells)

For (1), we can instead determine our directory using $BASH_SOURCE[0].
This is slightly differently than our previous behavior, where we'd
always load bash-preexec.sh from the $GHOSTTY_RESOURCES_DIR hierarchy,
even if ghostty.bash from source from somewhere else on the file system
... but we never relied on that behavior, even in development.

For (2), there's no harm in source'ing this script outside of Ghostty,
and if that does become a concern, we can restore this condition or use
something more targeted based on those specific cases.

Historically, I believe (2) was in place to enable (1), so addressing
(1) removes the need for (2).

And lastly, none of the other shell integration scripts depend on
$GHOSTTY_RESOURCES_DIR.
2025-06-16 19:54:27 -04:00
Jon Parise
314d52ac3a shell-integration: switch to $GHOSTTY_SHELL_FEATURES
This change consolidates all three opt-out shell integration environment
variables into a single opt-in $GHOSTTY_SHELL_FEATURES variable. Its
value is a comma-delimited list of the enabled shell feature names (e.g.
"cursor,title").

$GHOSTTY_SHELL_FEATURES is set at runtime and automatically added to the
shell environment. Its value is based on the shell-integration-features
configuration option.

$GHOSTTY_SHELL_FEATURES is only set when at least one shell feature is
enabled. It won't be set when 'shell-integration-features = false'.

$GHOSTTY_SHELL_FEATURES lists only the enabled shell feature names. We
could have alternatively gone in the opposite direction and listed the
disabled features, letting the scripts assume each feature is on by
default like we did before, but I think this explicit approach is a
little safer and easier to reason about / debug.

It also doesn't support the "no-" negation prefix used by the config
system (e.g. "cursor,no-title"). This simplifies the implementation
requirements of our (multiple) shell integration scripts, and because
$GHOSTTY_SHELL_FEATURES is derived from shell-integration-features,
the user-facing configuration interface retains that expressiveness.

$GHOSTTY_SHELL_FEATURES is intended to primarily be an internal concern:
an interface between the runtime and our shell integration scripts. It
could be used by people with particular use cases who want to manually
source those scripts, but that isn't the intended audience.

... and because the previous $GHOSTTY_SHELL_INTEGRATION_NO_* variables
were also meant to be an internal concern, this change does not include
backwards compatibility support for those names.

One last advantage of a using a single $GHOSTTY_SHELL_FEATURES variable
is that it can be easily forwarded to e.g. ssh sessions or other shell
environments.
2025-03-22 10:16:59 -04:00
Jon Parise
afa23532b6 bash: revert automatic shell integration changes
The intention of #5075 was to create a less intrusive, more hermetic
environment in which to source the bash startup files. This caused
problems for multiple people, and I believe that's because the general
expectation is that these files are sourced at global (not function)
scope.

For example, when a file is sourced from within a function scope, any
variables that weren't explicitly exported into the global environment
won't be available outside of the scope of the function. Most system and
personal startup files aren't written with that constraint because it's
not how bash itself loads these files.

As a small improvement over the original code, `rcfile` has been renamed
to `__ghostty_rcfile`. Avoiding leaking this variable while sourcing
these files was a goal of #5075, and prefixing it make it much less of a
potential issue.

This change also reverts the $HOME to ~/ change. While the ~/ notation
is more concise, using $HOME is more common and easier to implement
safely with regard to quoting.
2025-01-20 10:56:47 -05:00
Jon Parise
6af1850ab4 bash: less intrusive automatic shell integration
We now use a temporary function (__ghostty_bash_startup) to perform the
bash startup sequence. This gives us a local function scope in which to
store some temporary values (like rcfile). This way, they won't leak
into the sourced files' scopes.

Also, use `~/` instead of `$HOME` for home directory paths as a simpler
shorthand notation.
2025-01-16 08:11:26 -05:00
Mitchell Hashimoto
2f6bef7ff2 bash: drop automatic shell integration with --posix (#4785)
'--posix' starts bash in POSIX mode (like /bin/sh). This is rarely used
for interactive shells, and removing automatic shell integration support
for this option allows us to simply/remove some exceptional code paths.

Users are still able to manually source the shell integration script.

Also fix an issue where we would still inject GHOSTTY_BASH_RCFILE if we
aborted the automatic shell integration path _after_ seeing an --rcfile
or --init-file argument.
2025-01-07 20:45:04 -08:00
Mitchell Hashimoto
e00e991080 bash: set the title command in preexec (#4775)
PS0 is evaluated after a command is read but before it is executed. The
'preexec' hook (from bash-preexec) is equivalent for our title-updating
purposes and conveniently provides the current command as an argument
(from its own `history 1` call).
2025-01-07 20:34:56 -08:00
Jon Parise
1b91a667fb bash: drop automatic shell integration with --posix
'--posix' starts bash in POSIX mode (like /bin/sh). This is rarely used
for interactive shells, and removing automatic shell integration support
for this option allows us to simply/remove some exceptional code paths.

Users are still able to manually source the shell integration script.

Also fix an issue where we would still inject GHOSTTY_BASH_RCFILE if we
aborted the automatic shell integration path _after_ seeing an --rcfile
or --init-file argument.
2025-01-07 16:09:03 -05:00
Jon Parise
41201068ef bash: add license declaration for kitty-derived code 2025-01-07 11:34:39 -05:00
Jon Parise
bf3597a519 bash: set the title command in preexec
PS0 is evaluated after a command is read but before it is executed. The
'preexec' hook (from bash-preexec) is equivalent for our title-updating
purposes and conveniently provides the current command as an argument
(from its own `history 1` call).
2025-01-07 11:20:12 -05:00
Jon Parise
237c941395 bash: narrow the scope of GHOSTTY_BASH_ENV
GHOSTTY_BASH_ENV is only set in the '--posix' path. This change is a
code organization improvement and doesn't change the script's behavior.
2025-01-06 10:02:24 -05:00
Patrick Reynolds
5fa9e88482 Use \w instead of $PWD for title bar 2025-01-04 19:26:14 -05:00
Mitchell Hashimoto
1a27ce0797 bash: improved 'sudo' command wrapper (#4080)
The previous approach to wrapping `sudo` had a few shortcomings:

1. We were (re)defining our 'sudo' function wrapper in the "precmd"
path. It only needs to be defined once in the shell session.
2. If there was an existing 'sudo' alias, the function definition would
conflict and result in a syntax error.

Fix (1) by hoisting the 'sudo' function into global scope. I also
considered only defining our wrapper if an executable `sudo` binary
could be found (e.g. `-x $(builtin command -v sudo)`, but let's keep the
existing behavior for now. This allows for a `sudo` command to be
installed later in the shell session and still be wrapped.

Address (2) by defining the wrapper function using `function sudo`
(instead of `sudo()`) syntax. An explicit function definition won't
clash with an existing 'sudo' alias, although the alias will continue to
take precedence (i.e. our wrapper won't be called). If the alias is
defined _after_ our 'sudo' function is defined, our function will call
the aliased command.

This ordering is relevant because it can result in different behaviors
depending on when a user defines their aliases relative to sourcing the
shell integration script. Our recommendation remains that users either
use automatic shell injection or manually source the shell integration
script _before_ other things in their `.bashrc`, so that aligns with the
expected behavior of the 'sudo' wrapper with regard to aliases. Given
that, I don't think we need any more explicit user-facing documentation
on this beyond the script-level comments.
2024-12-30 08:44:47 -08:00
Jon Parise
0dc3ea35c0 bash: remove "request for experts" comment
We now have a multiple folks who have pitched in to improve this script.
2024-12-30 10:19:55 -05:00
Jon Parise
4e7982fc2b bash: improved 'sudo' command wrapper
The previous approach to wrapping `sudo` had a few shortcomings:

1. We were (re)defining our 'sudo' function wrapper in the "precmd"
   path. It only needs to be defined once in the shell session.
2. If there was an existing 'sudo' alias, the function definition would
   conflict and result in a syntax error.

Fix (1) by hoisting the 'sudo' function into global scope. I also
considered only defining our wrapper if an executable `sudo` binary
could be found (e.g. `-x $(builtin command -v sudo)`, but let's keep the
existing behavior for now. This allows for a `sudo` command to be
installed later in the shell session and still be wrapped.

Address (2) by defining the wrapper function using `function sudo`
(instead of `sudo()`) syntax. An explicit function definition won't
clash with an existing 'sudo' alias, although the alias will continue to
take precedence (i.e. our wrapper won't be called). If the alias is
defined _after_ our 'sudo' function is defined, our function will call
the aliased command.

This ordering is relevant because it can result in different behaviors
depending on when a user defines their aliases relative to sourcing the
shell integration script. Our recommendation remains that users either
use automatic shell injection or manually source the shell integration
script _before_ other things in their `.bashrc`, so that aligns with the
expected behavior of the 'sudo' wrapper with regard to aliases. Given
that, I don't think we need any more explicit user-facing documentation
on this beyond the script-level comments.
2024-12-30 10:15:04 -05:00
Jon Parise
76cd6b8b2e bash: standardize on shorthand escape sequences
We used a mix of shorthand and octal representations when printing these
control characters. Standardize on the shorter, more readable shorthand
notation because that's what we use in the other shell integration
scripts.
2024-12-21 10:19:58 -05:00
Jon Parise
63747d1e22 bash: improve clearing of multiline prompts
Bash doesn't redraw the leading lines in a multiline prompt so we mark
the last line as a secondary prompt (k=s) to prevent the preceding lines
from being erased by Ghostty after a resize.

Our previously attempt at this (#1973) was flawed. Instead, we now just
re-issue the OSC "133;A" command with a 'k=s' (secondary) kind at the
end of our prompt string.

This isn't a great solution because it stomps on the prompt's "133;B"
command (end of prompt and start of user input), but it's sufficient for
now and only applies in the multiline prompt case.

Going forward, we should revisit our semantic prompt implementation. Our
row-based approach is too limiting; lines can have multiple markers, and
markers should be recorded with their full coordinates so they can form
ranges.

See: https://per.bothner.com/blog/2019/shell-integration-proposal/
2024-12-17 16:28:41 -05:00
Anund
38b42fc970 shell-integration: add bashrc location lookup in Nixos
fixes #2979
2024-12-17 21:49:15 +11:00
Anthony
7faa73e5fd Remove shebangs from non-executable configuration source scripts 2024-11-15 02:15:09 +11:00
Anthony
387ce53a97 Revert "Update shebangs in installed shellscripts to meet rpmlint and match provided paths at [pkgs.org](https://fedora.pkgs.org/41/fedora-x86_64/elvish-0.21.0-4.fc41.x86_64.rpm.html)"
This reverts commit c936276781107904e9af810e52f3661e91adc629.
2024-11-15 02:13:54 +11:00
Anthony
c936276781 Update shebangs in installed shellscripts to meet rpmlint and match provided paths at [pkgs.org](https://fedora.pkgs.org/41/fedora-x86_64/elvish-0.21.0-4.fc41.x86_64.rpm.html) 2024-11-15 01:29:50 +11:00
Jon Parise
aa47047a6e bash: ghostty doesn't support OSC 133;P
Remove OSC 133;P marks in the basic prompts, and use OSC 133;A in the
multiline case.
2024-07-22 10:53:50 -04:00
Jon Parise
2bf1f80f77 bash: add primary and secondary marks to PS1 and PS2
These OSC 133 semantic prompt sequences mark the primary and secondary
parts of the prompt strings.

PS1 is marked as the primary (initial) prompt. This is the default, so
we could skip emitting these sequences, but they're added here for now
to be explicit and consistent with what other terminal emulators do in
their shell integrations.

If PS1 is a multiline prompt (i.e. it contains a newline), we mark the
last line as a secondary prompt (k=s). bash doesn't redraw the leading
lines of a multiline prompt on its own, so we can use this information
at runtime to prevent the preceding lines from being erased by ghostty
after a resize.

PS2 is always marked as a secondary prompt.
2024-07-19 07:44:41 -04:00
Jon Parise
00fe956b3b bash: always send "end of command" prompt sequence
I noticed that both wezterm[0] and the OSC 133 reference script[1] (upon
which wezterm and our bash integration appear to be based) always send
the "end of prompt" sequence in their precmd functions (except during
their first execution, when no previous command has been executed).

This is a subtle logical difference: `$_ghostty_executing=""` (first
run) versus `$_ghostty_executing="0"` (reentrancy check). I'm not sure
how much a difference it makes except in rare edge case scenarios, but I
think it makes sense to be consistent and always report the end of the
current command.

[0] https://github.com/wez/wezterm/blob/main/assets/shell-integration/wezterm.sh#L479
[1] https://gitlab.freedesktop.org/Per_Bothner/specifications/-/blob/master/proposals/prompts-data/shell-integration.bash
2024-07-18 10:31:41 -04:00
Jon Parise
11f3400e49 shell-integration: suppress shellcheck issues 2024-05-14 06:57:02 -07:00
Jon Parise
73b3560e62 shell-integration: automatic bash integration
This change adds automatic bash shell detection and integration.

Unlike our other shell integrations, bash doesn't provide a built-in
mechanism for injecting our ghostty.bash script into the new shell
environment.

Instead, we start bash in POSIX mode and use the ENV environment
variable to load our integration script, and the rest of the bash
startup sequence becomes the responsibility of our script to emulate
(along with disabling POSIX mode).
2024-05-05 13:59:52 -07:00