Pre-submit Checks
Describe the bug
When a block's command lists the contents of a directory other than the block's working directory — for example ls -la some/dir/, ls subdir, ls /etc/ — clickable filename detection in the block's output uses the block's CWD as the resolution root instead of the directory that was listed.
This produces three user-visible symptoms:
- Silent misresolution (most harmful). If a file by the same name as one listed in
ls DIR/ also exists in the block's CWD, clicking the filename in the output opens the wrong file (the one from CWD) without any indication. The most obvious tell is README.md: clicking README.md in ls subdir/ output opens the root README.md in whatever application is configured for Markdown files, not subdir/README.md.
- False negatives for files. If
DIR/foo.txt exists but CWD/foo.txt does not, foo.txt is not clickable at all in the ls DIR/ output.
- False negatives for directories. Subdirectories listed by
ls DIR/ are not clickable. (In contrast, directories listed by ls with no argument are clickable and open in Finder via SystemGeneric routing.)
Symptoms 2 and 3 are related to (but distinct from) the enhancement request in #4636. This report focuses on the silent-misresolution root cause that #4636 does not surface.
Related but distinct prior art: #4636 is an enhancement request for clickability of files outside CWD; this bug is about silent misresolution when a same-named file exists in CWD. #3439 is a feature request for right-click menus on ls output.
To reproduce
Symptom 1 — silent misresolution (the most important case):
# Set up
mkdir -p /tmp/warp-repro/subdir
echo "root readme" > /tmp/warp-repro/README.md
echo "subdir readme" > /tmp/warp-repro/subdir/README.md
cd /tmp/warp-repro
# Run in Warp
ls -la subdir/
# Output includes:
# -rw-r--r-- … README.md
# Click README.md in the ls output.
- Actual: Warp opens
/tmp/warp-repro/README.md ("root readme") in the Markdown viewer.
- Expected: Warp opens
/tmp/warp-repro/subdir/README.md ("subdir readme").
Symptom 2 — false negative for unique files:
cd /tmp/warp-repro
ls subdir/ # output includes 'README.md'
rm README.md # now only subdir/README.md exists
ls subdir/ # clicking README.md in the new block: nothing happens
Symptom 3 — directories in ls DIR/ output not clickable:
ls -la /tmp/warp-repro/ # subdir is clickable → opens Finder
ls -la / # /tmp is clickable → opens Finder
ls -la subdir/ # any subdirectory row: NOT clickable
Scrollback reproduction (to show the bug is stable across block history):
ls -la subdir/ # block A (CWD = /tmp/warp-repro)
cd /tmp # move away
# scroll back to block A, click README.md
- Actual: Same wrong
README.md opens (from /tmp/warp-repro, not from subdir/).
This is consistent with how per-block resolution works today: each block stores its own pwd, so scrollback is internally correct — the bug is that the stored pwd is the wrong resolution root when the command listed a different directory.
Expected behavior
When a block's command is a directory-listing command with a directory argument, clickable path detection in that block's output must use that directory (joined to the block's stored pwd) as the primary resolution root.
Specifically:
- Given block command
ls DIR/ (or ls -la DIR/, ls --color=always DIR/, etc.) with block.pwd = CWD, bare filename NAME in the output must resolve to CWD/DIR/NAME if that path exists.
- If both
CWD/NAME and CWD/DIR/NAME exist, the block's listed-argument wins. (There is no scenario where a user running ls DIR/ wants clicks on its output to open files outside DIR.)
- If only
CWD/DIR/NAME exists, NAME must be clickable.
- Directory entries in
ls DIR/ output must be clickable and open in Finder, matching the existing behavior for ls with no argument.
- The behavior must hold for scrollback blocks, including blocks restored from a previous session, because each block carries its own pwd and command.
- The fix must not affect blocks whose output is already rooted (e.g.
find DIR/ -name '*.md' output contains DIR/sub/foo.md, which already resolves correctly against CWD). Avoid double-joining.
Initial command coverage
The fix should start with ls (the most common case). A user-configurable list of listing-aware commands accommodates exa, eza, lsd, and similar modern replacements, and allows users to add their own. tree is a natural extension but is blocked by a separate tokenizer bug (box-drawing characters not recognized as link separators — tracked in a sibling issue).
Out of scope for this issue (explicitly defer to follow-ups):
ls -R DIR/ — recursive listings have multiple root contexts marked by DIR/SUB: headers; needs per-section resolution.
ls DIR1/ DIR2/ — multi-argument listings; needs per-section resolution.
- Piped/redirected output (
ls DIR/ > file; cat file) — argument context is lost at the pipe boundary.
Root cause (from source reading)
Pointers to the code paths so maintainers can sanity-check the framing:
app/src/terminal/view/link_detection.rs, scan_for_file_path: for BlockList clicks, the working directory passed to the resolver is block.pwd().map(String::from) — the CWD at the time the command ran. The block's command is not consulted.
app/src/util/file.rs, absolute_path_if_valid: joins the candidate to the working_directory passed in, or treats it as absolute. No awareness of any directory argument on the block's command.
app/src/terminal/model/grid/grid_handler.rs:1097, possible_file_paths_at_point: tokenizes the clicked row into candidate substrings. The tokenizer works correctly for bare filenames like README.md; the bug is strictly in the resolution step, not tokenization (for tree-style output, tokenization is also broken — see sibling issue).
app/src/terminal/model/block/serialized_block.rs: blocks serialize pwd and stylized_command, so the fix works durably across scrollback and session restore.
app/src/terminal/view/open_in_warp.rs, check_openable_in_warp: already uses warp_completer::parsers to extract positional arguments from commands in FILE_VIEWER_COMMANDS. The primary fix can reuse this parsing path for the new listing-command set.
Proposed fix — implementation summary
I've prototyped a fix on a local branch (rndjams/fix-ls-dir-link-resolution). Summary:
What's in the proposed PR:
-
A new pure-logic helper in crates/warp_util/src/listing_command.rs:
pub const DEFAULT_LISTING_COMMANDS: &[&str] = &["ls", "exa", "eza", "lsd"];
pub fn listing_command_argument_dir(
command: &str,
pwd: &Path,
listing_commands: &[&str],
) -> Option<PathBuf>;
Uses shlex::split for shell tokenization (handles '...', "...", backslash escapes). Skips leading KEY=VALUE env-var prefixes (LS_COLORS=auto ls DIR), skips flags (-*), returns the first path-like positional joined to pwd, gated on is_dir().
-
22 unit tests covering realistic ls variants: bare path, trailing slash, absolute path, tilde expansion, quoted args with spaces, multi-arg (first wins), flag+value combos like --color=always DIR, env-var prefixes, malformed input, custom command sets, negative tests for find/cat/git, plus 5 alias-specific tests exercising the resolved-name override path.
-
Wire-up in app/src/terminal/view/link_detection.rs:
scan_for_file_path reads the block's command + pwd and computes listing_dir_to_scan via the helper.
- The helper also accepts an alias-resolved command name via
Block::top_level_command(sessions), so user aliases like alias ll='ls -l' trigger the fix without needing ll in DEFAULT_LISTING_COMMANDS.
compute_valid_paths tries listing_dir resolution first via a new try_resolve closure; if that returns nothing, falls through to the existing ShellNative(working_directory) path. Behavior is strictly additive — non-listing commands and blocks without a directory argument are unaffected.
Deliberate scope boundaries (V1):
- Aliases with baked-in positional args are a known limitation. Single-level alias names are resolved via
Block::top_level_command(sessions) so alias ll='ls -l' → ll DIR/ correctly triggers the fix. But if an alias bakes in a positional (alias lsd='ls /tmp' → lsd DIR/), the helper picks the user-typed DIR/ as the first positional, not the aliased /tmp. Rare in practice; documented in the helper's doc-comment.
- No user-facing setting in V1.
DEFAULT_LISTING_COMMANDS is a const slice in warp_util. Making it a TOML setting is a separate, orthogonal concern and felt out of scope for a bug fix. Happy to add it in the same PR if maintainers prefer.
- Recursive listings (
ls -R DIR/, tree DIR) are out of scope. Each section header (SUB:) changes the resolution root; needs per-section tracking.
- Multi-arg listings (
ls DIR1/ DIR2/) are out of scope. First arg wins today.
- Piped/redirected output (
ls DIR/ > file; cat file) is a permanent limitation. The argument context is lost at the pipe boundary.
tree output tokenization is blocked on a separate tokenizer bug — box-drawing characters (│, ├, └, ─) aren't in FILE_LINK_SEPARATORS. Tracked in a sibling issue so tree benefits immediately once both fixes land.
Validation (local):
cargo test -p warp_util --lib listing_command — all 22 tests pass (17 original + 5 alias-specific).
cargo test -p warp --lib util:: — 98 existing tests pass. 1 unrelated pre-existing flake in util::git::tests::detached_tag_display_returns_short_sha (environment-sensitive, unrelated to this change).
cargo clippy -p warp --all-targets -- -D warnings — clean.
cargo fmt --all --check — clean.
Happy to open the PR once this issue is triaged.
Screenshots, videos, and logs
The repro steps above are minimal and deterministic; happy to record a short screen capture on request.
Operating system (OS)
macOS
Operating system and version
macOS 26.4.1 (Darwin 25.4.0, arm64)
Shell Version
zsh 5.9
Current Warp version
v0.2026.04.27.15.32.stable_03
Regression
No, this bug or issue has existed throughout my experience using Warp
Recent working Warp date
No response
Additional context
Sibling issue: tokenizer does not treat box-drawing characters (│, ├, └, ─) as link separators, preventing tree and eza --tree leaf filenames from being tokenized as clickable candidates. That issue is complementary to this one — even after listing-aware resolution lands, tree output will still be unclickable until the tokenizer is fixed.
Does this block you from using Warp daily?
No
Is this an issue only in Warp?
Yes, I confirmed that this only happens in Warp, not other terminals.
Warp Internal (ignore): linear-label:b9d78064-c89e-4973-b153-5178a31ee54e
None
Pre-submit Checks
Describe the bug
When a block's command lists the contents of a directory other than the block's working directory — for example
ls -la some/dir/,ls subdir,ls /etc/— clickable filename detection in the block's output uses the block's CWD as the resolution root instead of the directory that was listed.This produces three user-visible symptoms:
ls DIR/also exists in the block's CWD, clicking the filename in the output opens the wrong file (the one from CWD) without any indication. The most obvious tell isREADME.md: clickingREADME.mdinls subdir/output opens the rootREADME.mdin whatever application is configured for Markdown files, notsubdir/README.md.DIR/foo.txtexists butCWD/foo.txtdoes not,foo.txtis not clickable at all in thels DIR/output.ls DIR/are not clickable. (In contrast, directories listed bylswith no argument are clickable and open in Finder viaSystemGenericrouting.)Symptoms 2 and 3 are related to (but distinct from) the enhancement request in #4636. This report focuses on the silent-misresolution root cause that #4636 does not surface.
Related but distinct prior art: #4636 is an enhancement request for clickability of files outside CWD; this bug is about silent misresolution when a same-named file exists in CWD. #3439 is a feature request for right-click menus on
lsoutput.To reproduce
Symptom 1 — silent misresolution (the most important case):
/tmp/warp-repro/README.md("root readme") in the Markdown viewer./tmp/warp-repro/subdir/README.md("subdir readme").Symptom 2 — false negative for unique files:
Symptom 3 — directories in
ls DIR/output not clickable:Scrollback reproduction (to show the bug is stable across block history):
README.mdopens (from/tmp/warp-repro, not fromsubdir/).This is consistent with how per-block resolution works today: each block stores its own pwd, so scrollback is internally correct — the bug is that the stored pwd is the wrong resolution root when the command listed a different directory.
Expected behavior
When a block's command is a directory-listing command with a directory argument, clickable path detection in that block's output must use that directory (joined to the block's stored pwd) as the primary resolution root.
Specifically:
ls DIR/(orls -la DIR/,ls --color=always DIR/, etc.) withblock.pwd = CWD, bare filenameNAMEin the output must resolve toCWD/DIR/NAMEif that path exists.CWD/NAMEandCWD/DIR/NAMEexist, the block's listed-argument wins. (There is no scenario where a user runningls DIR/wants clicks on its output to open files outsideDIR.)CWD/DIR/NAMEexists,NAMEmust be clickable.ls DIR/output must be clickable and open in Finder, matching the existing behavior forlswith no argument.find DIR/ -name '*.md'output containsDIR/sub/foo.md, which already resolves correctly againstCWD). Avoid double-joining.Initial command coverage
The fix should start with
ls(the most common case). A user-configurable list of listing-aware commands accommodatesexa,eza,lsd, and similar modern replacements, and allows users to add their own.treeis a natural extension but is blocked by a separate tokenizer bug (box-drawing characters not recognized as link separators — tracked in a sibling issue).Out of scope for this issue (explicitly defer to follow-ups):
ls -R DIR/— recursive listings have multiple root contexts marked byDIR/SUB:headers; needs per-section resolution.ls DIR1/ DIR2/— multi-argument listings; needs per-section resolution.ls DIR/ > file; cat file) — argument context is lost at the pipe boundary.Root cause (from source reading)
Pointers to the code paths so maintainers can sanity-check the framing:
app/src/terminal/view/link_detection.rs,scan_for_file_path: for BlockList clicks, the working directory passed to the resolver isblock.pwd().map(String::from)— the CWD at the time the command ran. The block's command is not consulted.app/src/util/file.rs,absolute_path_if_valid: joins the candidate to theworking_directorypassed in, or treats it as absolute. No awareness of any directory argument on the block's command.app/src/terminal/model/grid/grid_handler.rs:1097,possible_file_paths_at_point: tokenizes the clicked row into candidate substrings. The tokenizer works correctly for bare filenames likeREADME.md; the bug is strictly in the resolution step, not tokenization (fortree-style output, tokenization is also broken — see sibling issue).app/src/terminal/model/block/serialized_block.rs: blocks serializepwdandstylized_command, so the fix works durably across scrollback and session restore.app/src/terminal/view/open_in_warp.rs,check_openable_in_warp: already useswarp_completer::parsersto extract positional arguments from commands inFILE_VIEWER_COMMANDS. The primary fix can reuse this parsing path for the new listing-command set.Proposed fix — implementation summary
I've prototyped a fix on a local branch (
rndjams/fix-ls-dir-link-resolution). Summary:What's in the proposed PR:
A new pure-logic helper in
crates/warp_util/src/listing_command.rs:Uses
shlex::splitfor shell tokenization (handles'...',"...", backslash escapes). Skips leadingKEY=VALUEenv-var prefixes (LS_COLORS=auto ls DIR), skips flags (-*), returns the first path-like positional joined to pwd, gated onis_dir().22 unit tests covering realistic
lsvariants: bare path, trailing slash, absolute path, tilde expansion, quoted args with spaces, multi-arg (first wins), flag+value combos like--color=always DIR, env-var prefixes, malformed input, custom command sets, negative tests forfind/cat/git, plus 5 alias-specific tests exercising the resolved-name override path.Wire-up in
app/src/terminal/view/link_detection.rs:scan_for_file_pathreads the block's command + pwd and computeslisting_dir_to_scanvia the helper.Block::top_level_command(sessions), so user aliases likealias ll='ls -l'trigger the fix without needingllinDEFAULT_LISTING_COMMANDS.compute_valid_pathstrieslisting_dirresolution first via a newtry_resolveclosure; if that returns nothing, falls through to the existingShellNative(working_directory)path. Behavior is strictly additive — non-listing commands and blocks without a directory argument are unaffected.Deliberate scope boundaries (V1):
Block::top_level_command(sessions)soalias ll='ls -l'→ll DIR/correctly triggers the fix. But if an alias bakes in a positional (alias lsd='ls /tmp'→lsd DIR/), the helper picks the user-typedDIR/as the first positional, not the aliased/tmp. Rare in practice; documented in the helper's doc-comment.DEFAULT_LISTING_COMMANDSis aconstslice inwarp_util. Making it a TOML setting is a separate, orthogonal concern and felt out of scope for a bug fix. Happy to add it in the same PR if maintainers prefer.ls -R DIR/,tree DIR) are out of scope. Each section header (SUB:) changes the resolution root; needs per-section tracking.ls DIR1/ DIR2/) are out of scope. First arg wins today.ls DIR/ > file; cat file) is a permanent limitation. The argument context is lost at the pipe boundary.treeoutput tokenization is blocked on a separate tokenizer bug — box-drawing characters (│,├,└,─) aren't inFILE_LINK_SEPARATORS. Tracked in a sibling issue sotreebenefits immediately once both fixes land.Validation (local):
cargo test -p warp_util --lib listing_command— all 22 tests pass (17 original + 5 alias-specific).cargo test -p warp --lib util::— 98 existing tests pass. 1 unrelated pre-existing flake inutil::git::tests::detached_tag_display_returns_short_sha(environment-sensitive, unrelated to this change).cargo clippy -p warp --all-targets -- -D warnings— clean.cargo fmt --all --check— clean.Happy to open the PR once this issue is triaged.
Screenshots, videos, and logs
The repro steps above are minimal and deterministic; happy to record a short screen capture on request.
Operating system (OS)
macOS
Operating system and version
macOS 26.4.1 (Darwin 25.4.0, arm64)
Shell Version
zsh 5.9
Current Warp version
v0.2026.04.27.15.32.stable_03
Regression
No, this bug or issue has existed throughout my experience using Warp
Recent working Warp date
No response
Additional context
Sibling issue: tokenizer does not treat box-drawing characters (
│,├,└,─) as link separators, preventingtreeandeza --treeleaf filenames from being tokenized as clickable candidates. That issue is complementary to this one — even after listing-aware resolution lands,treeoutput will still be unclickable until the tokenizer is fixed.Does this block you from using Warp daily?
No
Is this an issue only in Warp?
Yes, I confirmed that this only happens in Warp, not other terminals.
Warp Internal (ignore): linear-label:b9d78064-c89e-4973-b153-5178a31ee54e
None