fix(gateway): treat a leading @/ as a separator, not just an absolute path

`@/Desktop` returned nothing while `@Desktop` worked. The leading slash
was always read literally, so the lookup went to the absolute `/Desktop`
— which doesn't exist — and dead-ended instead of finding the folder one
level down.

The `@` has already announced "this is a path", so the slash people type
next reads as a separator out of habit rather than a filesystem root.
Take the absolute meaning only when it resolves: the parent directory has
to exist, and a partially-typed segment has to match something in it.
Otherwise drop the slash and resolve from the cwd.

Real absolute paths are unaffected — `/usr`, `/etc/hos`, `/Users/...` all
pass the existence probe and keep their current behaviour. The guard
matters: stripping the slash unconditionally would let a repo-local
`etc/` shadow the real `/etc`.
This commit is contained in:
Brooklyn Nicholson 2026-07-27 21:07:49 -05:00
parent 71e7eb3c16
commit 044cf46a0d
2 changed files with 102 additions and 0 deletions

View file

@ -353,3 +353,65 @@ def test_fuzzy_finds_top_level_entries_outside_a_git_repo(tmp_path, monkeypatch)
(tmp_path / "Desktop" / "note.txt").write_text("x")
assert "@folder:Desktop/" in [t for t, _, _ in _items("@Desktop")]
# ── Leading slash is a separator, not necessarily an absolute path ───────
# `@/Desktop` used to dead-end: it was read as the absolute `/Desktop`,
# which doesn't exist. People type the slash out of habit — the `@` already
# announced "this is a path" — so it should mean the same as `@Desktop`
# unless a real absolute path is there.
def test_leading_slash_matches_the_bare_form(tmp_path, monkeypatch):
"""`@/foo` and `@foo` return the same thing when `/foo` doesn't exist."""
monkeypatch.chdir(tmp_path)
(tmp_path / "Desktop").mkdir()
(tmp_path / "Desktop" / "note.txt").write_text("x")
server._fuzzy_cache.clear()
bare = [t for t, _, _ in _items("@Desktop")]
server._fuzzy_cache.clear()
slashed = [t for t, _, _ in _items("@/Desktop")]
assert "@folder:Desktop/" in bare
assert slashed == bare
def test_leading_slash_navigates_into_subfolders(tmp_path, monkeypatch):
"""The fallback survives deeper paths, not just a single segment."""
monkeypatch.chdir(tmp_path)
(tmp_path / "apps" / "desktop").mkdir(parents=True)
(tmp_path / "apps" / "desktop" / "main.tsx").write_text("x")
assert "@folder:apps/desktop/" in [t for t, _, _ in _items("@/apps/desktop")]
server._fuzzy_cache.clear()
assert any("main.tsx" in t for t, _, _ in _items("@/apps/desktop/"))
def test_leading_slash_prefers_a_real_absolute_path(tmp_path, monkeypatch):
"""When the absolute reading resolves, it wins — no silent rewrite.
A cwd-relative `usr/` must not shadow the real `/usr`, or typing an
absolute path in a repo that happens to mirror those names breaks.
"""
monkeypatch.chdir(tmp_path)
# A decoy that would win if the slash were stripped unconditionally.
(tmp_path / "etc").mkdir()
(tmp_path / "etc" / "decoy.conf").write_text("x")
texts = [t for t, _, _ in _items("@/etc/")]
# `/etc` exists on any POSIX box, so the absolute reading must hold.
assert not any("decoy.conf" in t for t in texts), texts
def test_leading_slash_falls_back_only_when_absolute_is_missing(tmp_path, monkeypatch):
"""A nonexistent absolute prefix falls back; an existing one doesn't."""
monkeypatch.chdir(tmp_path)
(tmp_path / "nonexistent-at-root").mkdir()
(tmp_path / "nonexistent-at-root" / "f.txt").write_text("x")
assert "@folder:nonexistent-at-root/" in [
t for t, _, _ in _items("@/nonexistent-at-root")
]

View file

@ -16136,6 +16136,31 @@ def _fuzzy_basename_rank(name: str, query: str) -> tuple[int, int] | None:
return None
def _abs_completion_prefix_exists(path_part: str) -> bool:
"""True when ``path_part`` reads sensibly as an absolute path.
A leading `/` is only meant literally if something is actually there:
the parent directory has to exist, and a partially-typed final segment
has to match at least one of its entries. Used to decide whether
`@/foo` is the absolute `/foo` or shorthand for `foo` under the cwd.
"""
expanded = _normalize_completion_path(path_part)
parent = os.path.dirname(expanded.rstrip("/")) or "/"
tail = os.path.basename(expanded.rstrip("/"))
if not os.path.isdir(parent):
return False
if not tail or expanded.endswith("/"):
return os.path.isdir(expanded) or expanded == "/"
try:
tail_lower = tail.lower()
return any(e.lower().startswith(tail_lower) for e in os.listdir(parent))
except OSError:
return False
@method("complete.path")
def _(rid, params: dict) -> dict:
word = params.get("word", "")
@ -16171,6 +16196,21 @@ def _(rid, params: dict) -> dict:
prefix_tag = ""
path_part = query if is_context else query
# `@/foo` almost always means "foo, from here" rather than the absolute
# `/foo`: the `@` already says "this is a path", so the slash reads as a
# separator people type out of habit. Take the absolute reading only
# when something is actually there, else drop the slash and resolve
# relative to the cwd — otherwise `@/Desktop` dead-ends on a directory
# that exists one level down. Real absolute paths (`@/usr/local`,
# `@/etc/hosts`) still resolve, since those prefixes do exist.
if (
is_context
and path_part.startswith("/")
and not path_part.startswith("//")
and not _abs_completion_prefix_exists(path_part)
):
path_part = path_part.lstrip("/")
# Fuzzy basename search across the repo when the user types a bare
# name with no path separator — `@appChrome` surfaces every file
# whose basename matches, regardless of directory depth. Matches what