Skip to content

fix: disable unsupported ssh shell-integration features - #475

Merged
umputun merged 2 commits into
masterfrom
ssh-wrapper-463
Aug 21, 2026
Merged

fix: disable unsupported ssh shell-integration features#475
umputun merged 2 commits into
masterfrom
ssh-wrapper-463

Conversation

@umputun

@umputun umputun commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Fix #463.

ghostty's shell integration implements the ssh-env and ssh-terminfo features by replacing the user's ssh with a wrapper that calls "$GHOSTTY_BIN_DIR/ghostty" +ssh. agterm's bundle has no ghostty CLI, so from 0.24.0 every ssh inside agterm died instantly once either flag was on:

fish: Unknown command: /Applications/agterm.app/Contents/MacOS/ghostty

Both flags default off, so only someone who deliberately turned them on is affected. The report is exact, including the footgun in its own workaround.

Where it came from. Upstream 283dca13 replaced the self-contained ssh block with an unguarded delegate on 2026-05-05, five days after our previous pin. 94c1407c moved GHOSTTY_REV onto it on Aug 16 and 0.24.0 shipped the next day. Upstream main still carries the unguarded call, so moving the pin forward does not help.

The fix is the third option the reporter proposed: apply the flags after the user's config so the wrapper is never defined. agterm loads a final shell-integration-features line with both values off, built from libghostty's own resolved value read back through ghostty_config_get after ghostty_config_load_recursive_files. ghostty therefore stays the only thing parsing config text, and a flag set behind a config-file include cannot land after the override.

The line names all six flags rather than just the two. ghostty re-parses this key from its defaults on every occurrence, so a bare no-ssh-env would reset everything else the user set - which is exactly what the reporter hit writing his workaround.

ShellIntegrationFeatureBitsTests pins the flag-to-bit mapping against the bundled libghostty: the default mask, each flag against its own bit in both directions, and = true covering exactly the six flags we know about. The last one is what catches a flag appended upstream, which the others would survive silently.

Verified on a running instance, not only by unit test. An isolated build whose scoped config was just config-file = child.conf, with the child setting shell-integration-features = no-path,ssh-terminfo, produced GHOSTTY_SHELL_FEATURES=cursor:blink,title and ssh is /usr/bin/ssh - the ssh flag stripped through the include, the user's no-path preserved, and no wrapper function defined.

Four surfaces now name the exception: the starter ghostty.conf, docs/troubleshooting.md, site/docs.html, and the bundled agent skill, which is the source for the Claude and Codex copies Help ▸ Install writes. Without that last one an installed agent asked to enable ssh-terminfo would write it, reload, see a diagnostic count of 0 and report success on a setting that does nothing.

The second commit is unrelated to the issue: it tracks .revmux/profile.md, the review calibration for this repo, and narrows the .revmux/ ignore so the run archive stays out.

Not covered: terminfo forwarding is gone rather than restored. Getting it back means either shipping a ghostty CLI or carrying a patch against upstream's shell integration, and neither is worth it for a feature that was never advertised. docs/troubleshooting.md gives the manual infocmp -x xterm-ghostty | ssh <host> 'tic -x -' instead.

ghostty implements `ssh-env` and `ssh-terminfo` by replacing `ssh` with a wrapper that calls a
`ghostty` CLI absent from agterm's bundle. Enabling either option made every `ssh` invocation fail.

read the resolved feature mask after recursive config loading, force the two unsupported bits off,
and restate every known flag so the other choices survive ghostty's reset-from-defaults assignment
semantics. Hosted tests pin the bit mapping and total feature count against the bundled libghostty.

Fix #463
`.revmux/profile.md` calibrates every revmux review of this repo: what agterm is, what a real failure
looks like here, the blast radius, and which conventions are deliberate rather than defects. Without it
each review runs on generic calibration and flags the short-comment policy and the visibility rule as
problems.

`.revmux/` was ignored wholesale for the task and run archive, and a re-include under an excluded
directory is unreachable, so the pattern becomes `.revmux/*` with the profile as the one exception.
The archive stays ignored.
Copilot AI lite review requested due to automatic review settings August 21, 2026 03:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@umputun
umputun merged commit 3811737 into master Aug 21, 2026
8 checks passed
@umputun
umputun deleted the ssh-wrapper-463 branch August 21, 2026 03:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ssh shell-integration wrapper is broken: $GHOSTTY_BIN_DIR/ghostty does not exist in the bundle

2 participants