Skip to content

Follow symlinks in Tab path completion (remote/SSH sessions) - #14746

Merged
acarl005 merged 3 commits into
masterfrom
factory/follow-symlinks-tab-complete
Aug 5, 2026
Merged

Follow symlinks in Tab path completion (remote/SSH sessions)#14746
acarl005 merged 3 commits into
masterfrom
factory/follow-symlinks-tab-complete

Conversation

@warp-agent-staging

@warp-agent-staging warp-agent-staging Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Description

In a remote / Warpified (SSH) session, Warp lists a directory for path completion by running a find script (ls_script_for_dir in app/src/completer/mod.rs). It used find . -maxdepth 1 -type d for directories and find . -maxdepth 1 -not -type d for files. find without -L does not follow symlinks when evaluating -type, so a symlink pointing at a directory failed -type d and was bucketed as a file — it was not offered as a directory completion (no trailing separator, excluded from cd directory completions). Adding -L to both invocations makes find follow symlinks, so a symlink to a directory is classified as a directory (matching a standard terminal), and a symlink to a file still completes as a file.
Scope: this PR fixes remote / Warpified SSH sessions only.

  • Plain local macOS/Linux was already correct: the local listing path uses EngineDirEntry::try_from, which follows a symlink via path().metadata() (CORE-3402). It is unchanged by this PR.
  • WSL is a separate defect and remains open. A WSL session resolves to SessionType::Local (same host, not SSH) and lists via std::fs::read_dir over the \\wsl$\... host view, not via this find script, so -L does not affect it. WSL is tracked separately on APP-3993.
    Refs Follow symlinks with Tab autocomplete (Linux client) #4498 (partially — the remote/SSH portion).

Linked Issue

Refs #4498

  • The linked issue is labeled ready-to-spec or ready-to-implement.
  • Where appropriate, screenshots or a short video of the implementation are included below (especially for user-visible or UI changes).

Testing

  • Added test_session_context_follows_symlinked_directories_remotely, which builds a real directory, a symlink to it, a file, and a symlink to that file, then runs the actual remote listing (the find script via a bash subprocess). It fails before the -L change (the directory symlink is classified as File) and passes after — both states confirmed.
  • ./script/format --check, cargo clippy -p warp --all-targets --tests -- -D warnings, and cargo nextest run -p warp completer:: all pass.
  • Verified end-to-end in the running app. Built and launched Warp, then created a loopback SSH session (ssh -p 2222 localhost), which Warp auto-Warpified into a remote session (confirmed by the agent@<host> user@host chip on the input line and the tab renamed to <host>:~, i.e. the session actually exercises the remote ls_script_for_dir path this PR changes). In ~/symlinktest (containing linkdirrealdir), typing cd link and pressing Tab completed to cd linkdir/ — the symlinked directory is offered as a directory (trailing separator, folder icon, "Directory" label), identical to the real realdir/. See the recording and screenshots below.

Screenshots / Videos

Computer-use video recordings

Launching Warp, running echo, connecting via loopback SSH on port 2222, checking for a Warpified/remote indicator, then testing Tab completion of "cd link" in ~/symlinktest.
Warp SSH warpify & Tab-completion test: Launching Warp, running echo, connecting via loopback SSH on port 2222, checking for a Warpified/remote indicator, then testing Tab completion of "cd link" in ~/symlinktest.

Computer-use screenshots (3)

Warp after the loopback ssh -p 2222 localhost command: the input line shows an "agent@t72g7k6t7vtma" user@host chip, the tab is renamed "t72g7k6t7vtma:~", and the remote login banner rendered as a Warp block headed "agent@t72g7k6t7vtma:~".
Warp after the loopback ssh -p 2222 localhost command: the input line shows an "agent@t72g7k6t7vtma" user@host chip, the tab is renamed "t72g7k6t7vtma:~", and the remote login banner rendered as a Warp block headed "agent@t72g7k6t7vtma:~".

Result of typing "cd link" and pressing Tab in the Warpified SSH session: it auto-completed inline to "cd linkdir/" (with trailing slash, treating the symlink as a directory); no popup list appeared because linkdir was the only directory match.
Result of typing "cd link" and pressing Tab in the Warpified SSH session: it auto-completed inline to "cd linkdir/" (with trailing slash, treating the symlink as a directory); no popup list appeared because linkdir was the only directory match.

Warp "cd " + Tab completion menu in the SSH session showing two entries, "linkdir/" and "realdir/", each with a folder icon, trailing slash, and the label "Directory"; the symlink linkdir is presented identically to the real directory, while linkfile/realfile.txt are not offered by cd completion.
Warp "cd " + Tab completion menu in the SSH session showing two entries, "linkdir/" and "realdir/", each with a folder icon, trailing slash, and the label "Directory"; the symlink linkdir is presented identically to the real directory, while linkfile/realfile.txt are not offered by cd completion.

  • I have manually tested my changes locally with ./script/run

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

CHANGELOG-IMPROVEMENT: Tab autocomplete now follows symlinks to directories in remote/SSH sessions.
Refs #4498
Originating thread: https://warpdev.slack.com/archives/C0BDQDW8V5E/p1785963086148259

Conversation: https://staging.warp.dev/conversation/a9e89e2e-4f9a-4b93-894b-d15a326cacb8
Run: https://oz.staging.warp.dev/runs/019fd3be-69c6-72ae-aea4-bdb5ba1ca004

This PR was generated with Oz.

Symlinks pointing at directories were classified as files in two completer
code paths, so they were not offered as directory completions:

- Remote/Warpified sessions: the find script used to list a directory
  omitted -L, so a symlink-to-directory failed -type d and landed in the
  files bucket. Add -L to both find invocations.
- WSL (and other emulated) sessions: the local listing follows a symlink
  via a host-side metadata() call, which fails when the target lives in the
  guest path space (e.g. /mnt/c/...). Fall back to resolving the link target
  through the session before deciding whether it is a directory.

Plain local Linux/mac behavior is unchanged (CORE-3402). Broken links and
loops still classify as non-directories, so completion never breaks.

Adds remote and local symlinked-directory regression tests.

Co-Authored-By: Warp Agent <agent@warp.dev>
@cla-bot cla-bot Bot added the cla-signed label Aug 5, 2026
@warp-agent-staging warp-agent-staging Bot added the warpy-factory Label associated to the "Wob the Wuilder" factory on staging, also known as Warpy Factory. label Aug 5, 2026
@warp-agent-staging
warp-agent-staging Bot marked this pull request as ready for review August 5, 2026 21:46
@warp-agent-staging
warp-agent-staging Bot requested a review from acarl005 August 5, 2026 21:46
@acarl005 acarl005 self-assigned this Aug 5, 2026
oz-agent and others added 2 commits August 5, 2026 22:27
Comments-only change (no behavioral or test-logic changes). Rewrites the
comments added for the symlink completion fix to follow AGENTS.md: drop
internal-step narration and transformation ("used to") phrasing, keep only
the non-obvious why, and reflow to the 100-column width.

Co-Authored-By: Warp Agent <agent@warp.dev>
Split per requester: this PR fixes remote/Warpified SSH symlink completion
only. Remove the emulated-session (WSL, MSYS2) fallback and its tests;
local macOS/Linux already follows symlinks via EngineDirEntry::try_from and
is untouched. WSL remains open and is tracked on APP-3993.

Co-Authored-By: Warp Agent <agent@warp.dev>
@warp-agent-staging warp-agent-staging Bot changed the title Follow symlinks in Tab path completion (remote + WSL) Follow symlinks in Tab path completion (remote/SSH sessions) Aug 5, 2026

@warp-agent-staging warp-agent-staging Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Overview

This PR adds -L to both find invocations in ls_script_for_dir so remote / Warpified (SSH) Tab completion classifies a symlink-to-directory as a directory, plus a regression test that drives the real remote listing path. The code change is correct, tested, and CI-green for the remote/SSH scope; the only blocker is a scope decision that a human must make (below).

Concerns

❓ [QUESTION] Scope vs. the ticket's acceptance criteria. APP-5190's acceptance criteria include a WSL case ("in a WSL session, a symlinked directory — including targets under /mnt/c/... — tab-completes as a directory"), and the active reports on issue #4498 — including the requester who tested this branch — are on WSL. This PR intentionally fixes only remote/Warpified SSH sessions and defers WSL to APP-3993 (WSL resolves to SessionType::Local and lists via std::fs::read_dir over the \\wsl$\... host view, so -L does not reach it). That descope is honest and clearly documented, but it means the reporter's own case is not fixed by this PR. Is landing the remote/SSH-only fix under APP-5190 (with WSL tracked separately on APP-3993) acceptable, or should APP-5190 remain open until the WSL path is also addressed? This is a scope/product decision, not a code change.

Verdict

Checks: build ✅ (CI) · tests ✅ (CI: Linux/macOS/Windows) · clippy+fmt ✅ (CI) · visual proof ✅ (remote/SSH, Warpified indicators shown) — local build/test gate not run: OOM-killed on the review runner (environment limitation, not the change).
Found: 0 critical, 0 important, 1 question

Request changes (blocked pending a human decision on scope; the delivered code has no code-level findings)

Review run

https://oz.staging.warp.dev/runs/019fd3e6-c83d-7cc9-b10a-733a1627cb0e

@acarl005
acarl005 merged commit 1b65a8b into master Aug 5, 2026
38 checks passed
@acarl005
acarl005 deleted the factory/follow-symlinks-tab-complete branch August 5, 2026 23:47
warp-agent-staging Bot pushed a commit that referenced this pull request Aug 6, 2026
…n round-trip

Reporter logs (from the instrumentation this branch added) established the mechanism: a WSL
session lists via SessionType::Local, the entry is a symlink, but value.path().metadata()
returns Err(NotFound) because the target is an IO_REPARSE_TAG_LX_SYMLINK the Windows host cannot
follow. std::fs::read_link cannot read that reparse tag either (Rust std only handles
IO_REPARSE_TAG_SYMLINK / MOUNT_POINT), so host-side resolution is impossible and unwrap_or(false)
bucketed the symlink as a file.

Fix (surgical, guest-side): after the host std::fs listing, if the session is emulated (WSL/MSYS2)
and any symlink was left unclassified as a directory, ask the guest which immediate children are
directories with `cd <dir> && find -L . -maxdepth 1 -type d -print0` (the same -L shape that fixed
the remote path in #14746) and upgrade the matching symlink entries. Nothing from the listing is
interpolated into the command, so there is no filename-quoting or injection surface.

Latency: the command runs only for emulated sessions and only when an unresolved directory symlink
is present; it is awaited on the async completion path (the sibling WarpifiedRemote branch already
awaits a guest command here) and bounded with with_timeout, degrading to the host classification
(symlink shown as a file) on timeout/error rather than stalling completion. Results are cached.
Plain local macOS/Linux is unaffected.

Keeps one temporary debug-gated read_link probe so the reporter's verification run also records
read_link's actual result on their WSL host. Adds unit tests for the output parsing and the
directory-symlink upgrade (the Err-from-metadata case); the local try_from behavior-preservation
test is retained.

Co-Authored-By: Warp Agent <agent@warp.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed warpy-factory Label associated to the "Wob the Wuilder" factory on staging, also known as Warpy Factory.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants